summaryrefslogtreecommitdiffstats
path: root/drivers/clk/at91
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/at91')
-rw-r--r--drivers/clk/at91/Makefile7
-rw-r--r--drivers/clk/at91/at91rm9200.c218
-rw-r--r--drivers/clk/at91/at91sam9260.c83
-rw-r--r--drivers/clk/at91/at91sam9g45.c237
-rw-r--r--drivers/clk/at91/at91sam9n12.c264
-rw-r--r--drivers/clk/at91/at91sam9rl.c59
-rw-r--r--drivers/clk/at91/at91sam9x5.c96
-rw-r--r--drivers/clk/at91/clk-audio-pll.c148
-rw-r--r--drivers/clk/at91/clk-generated.c118
-rw-r--r--drivers/clk/at91/clk-h32mx.c38
-rw-r--r--drivers/clk/at91/clk-i2s-mux.c46
-rw-r--r--drivers/clk/at91/clk-main.c285
-rw-r--r--drivers/clk/at91/clk-master.c433
-rw-r--r--drivers/clk/at91/clk-peripheral.c197
-rw-r--r--drivers/clk/at91/clk-pll.c90
-rw-r--r--drivers/clk/at91/clk-plldiv.c64
-rw-r--r--drivers/clk/at91/clk-programmable.c78
-rw-r--r--drivers/clk/at91/clk-sam9x60-pll.c731
-rw-r--r--drivers/clk/at91/clk-slow.c48
-rw-r--r--drivers/clk/at91/clk-smd.c75
-rw-r--r--drivers/clk/at91/clk-system.c72
-rw-r--r--drivers/clk/at91/clk-usb.c187
-rw-r--r--drivers/clk/at91/clk-utmi.c190
-rw-r--r--drivers/clk/at91/dt-compat.c727
-rw-r--r--drivers/clk/at91/pmc.c223
-rw-r--r--drivers/clk/at91/pmc.h170
-rw-r--r--drivers/clk/at91/sam9x60.c169
-rw-r--r--drivers/clk/at91/sama5d2.c151
-rw-r--r--drivers/clk/at91/sama5d3.c270
-rw-r--r--drivers/clk/at91/sama5d4.c98
-rw-r--r--drivers/clk/at91/sama7g5.c1133
-rw-r--r--drivers/clk/at91/sckc.c280
32 files changed, 4657 insertions, 2328 deletions
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 423605f452..083555b4d2 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -15,8 +15,11 @@ obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o
obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o
-obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o dt-compat.o
+obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9g45.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9n12.o at91sam9x5.o
obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
+obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o
obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
-obj-$(CONFIG_SOC_SAMA5D3) += dt-compat.o
obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
diff --git a/drivers/clk/at91/at91rm9200.c b/drivers/clk/at91/at91rm9200.c
new file mode 100644
index 0000000000..df75a93edb
--- /dev/null
+++ b/drivers/clk/at91/at91rm9200.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
+#include <mfd/syscon.h>
+#include <linux/slab.h>
+#include <stdio.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static DEFINE_SPINLOCK(rm9200_mck_lock);
+
+struct sck {
+ char *n;
+ char *p;
+ u8 id;
+};
+
+struct pck {
+ char *n;
+ u8 id;
+};
+
+static const struct clk_master_characteristics rm9200_mck_characteristics = {
+ .output = { .min = 0, .max = 80000000 },
+ .divisors = { 1, 2, 3, 4 },
+};
+
+static u8 rm9200_pll_out[] = { 0, 2 };
+
+static const struct clk_range rm9200_pll_outputs[] = {
+ { .min = 80000000, .max = 160000000 },
+ { .min = 150000000, .max = 180000000 },
+};
+
+static const struct clk_pll_characteristics rm9200_pll_characteristics = {
+ .input = { .min = 1000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(rm9200_pll_outputs),
+ .output = rm9200_pll_outputs,
+ .out = rm9200_pll_out,
+};
+
+static const struct sck at91rm9200_systemck[] = {
+ { .n = "udpck", .p = "usbck", .id = 1 },
+ { .n = "uhpck", .p = "usbck", .id = 4 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+ { .n = "pck2", .p = "prog2", .id = 10 },
+ { .n = "pck3", .p = "prog3", .id = 11 },
+};
+
+static const struct pck at91rm9200_periphck[] = {
+ { .n = "pioA_clk", .id = 2 },
+ { .n = "pioB_clk", .id = 3 },
+ { .n = "pioC_clk", .id = 4 },
+ { .n = "pioD_clk", .id = 5 },
+ { .n = "usart0_clk", .id = 6 },
+ { .n = "usart1_clk", .id = 7 },
+ { .n = "usart2_clk", .id = 8 },
+ { .n = "usart3_clk", .id = 9 },
+ { .n = "mci0_clk", .id = 10 },
+ { .n = "udc_clk", .id = 11 },
+ { .n = "twi0_clk", .id = 12 },
+ { .n = "spi0_clk", .id = 13 },
+ { .n = "ssc0_clk", .id = 14 },
+ { .n = "ssc1_clk", .id = 15 },
+ { .n = "ssc2_clk", .id = 16 },
+ { .n = "tc0_clk", .id = 17 },
+ { .n = "tc1_clk", .id = 18 },
+ { .n = "tc2_clk", .id = 19 },
+ { .n = "tc3_clk", .id = 20 },
+ { .n = "tc4_clk", .id = 21 },
+ { .n = "tc5_clk", .id = 22 },
+ { .n = "ohci_clk", .id = 23 },
+ { .n = "macb0_clk", .id = 24 },
+};
+
+static void __init at91rm9200_pmc_setup(struct device_node *np)
+{
+ const char *slowxtal_name, *mainxtal_name;
+ struct pmc_data *at91rm9200_pmc;
+ u32 usb_div[] = { 1, 2, 0, 0 };
+ const char *parent_names[6];
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ int i;
+ bool bypass;
+
+ i = of_property_match_string(np, "clock-names", "slow_xtal");
+ if (i < 0)
+ return;
+
+ slowxtal_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = device_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ at91rm9200_pmc = pmc_data_allocate(PMC_PLLBCK + 1,
+ nck(at91rm9200_systemck),
+ nck(at91rm9200_periphck), 0, 4);
+ if (!at91rm9200_pmc)
+ return;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91rm9200_pmc->chws[PMC_MAIN] = hw;
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ &at91rm9200_pll_layout,
+ &rm9200_pll_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91rm9200_pmc->chws[PMC_PLLACK] = hw;
+
+ hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
+ &at91rm9200_pll_layout,
+ &rm9200_pll_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91rm9200_pmc->chws[PMC_PLLBCK] = hw;
+
+ parent_names[0] = slowxtal_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "pllack";
+ parent_names[3] = "pllbck";
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91rm9200_master_layout,
+ &rm9200_mck_characteristics,
+ &rm9200_mck_lock);
+ 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;
+
+ at91rm9200_pmc->chws[PMC_MCK] = hw;
+
+ hw = at91rm9200_clk_register_usb(regmap, "usbck", "pllbck", usb_div);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slowxtal_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "pllack";
+ parent_names[3] = "pllbck";
+ for (i = 0; i < 4; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 4, i,
+ &at91rm9200_programmable_layout,
+ NULL);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91rm9200_pmc->pchws[i] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91rm9200_systemck); i++) {
+ hw = at91_clk_register_system(regmap, at91rm9200_systemck[i].n,
+ at91rm9200_systemck[i].p,
+ at91rm9200_systemck[i].id, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91rm9200_pmc->shws[at91rm9200_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91rm9200_periphck); i++) {
+ hw = at91_clk_register_peripheral(regmap,
+ at91rm9200_periphck[i].n,
+ "masterck_div",
+ at91rm9200_periphck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91rm9200_pmc->phws[at91rm9200_periphck[i].id] = hw;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91rm9200_pmc);
+
+ return;
+
+err_free:
+ kfree(at91rm9200_pmc);
+}
+/*
+ * While the TCB can be used as the clocksource, the system timer is most likely
+ * to be used instead. However, the pinctrl driver doesn't support probe
+ * deferring properly. Once this is fixed, this can be switched to a platform
+ * driver.
+ */
+CLK_OF_DECLARE(at91rm9200_pmc, "atmel,at91rm9200-pmc", at91rm9200_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9260.c b/drivers/clk/at91/at91sam9260.c
index 066dedf2a1..c94cd95566 100644
--- a/drivers/clk/at91/at91sam9260.c
+++ b/drivers/clk/at91/at91sam9260.c
@@ -1,13 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
@@ -38,6 +33,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 },
@@ -224,8 +221,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[] = {
@@ -339,7 +336,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
const char *parent_names[6];
const char *slck_name;
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -358,9 +355,10 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
if (IS_ERR(regmap))
return;
- at91sam9260_pmc = pmc_data_allocate(PMC_MAIN + 1,
+ at91sam9260_pmc = pmc_data_allocate(PMC_PLLBCK + 1,
ndck(data->sck, data->num_sck),
- ndck(data->pck, data->num_pck), 0);
+ ndck(data->pck, data->num_pck),
+ 0, data->num_progck);
if (!at91sam9260_pmc)
return;
@@ -378,7 +376,10 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
at91sam9260_pmc->chws[PMC_MAIN] = hw;
if (data->has_slck) {
- hw = clk_fixed("slow_rc_osc", 32768);
+ hw = clk_hw_register_fixed_rate_with_accuracy(NULL,
+ "slow_rc_osc",
+ NULL, 0, 32768,
+ 50000000);
if (IS_ERR(hw))
goto err_free;
@@ -401,19 +402,34 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
if (IS_ERR(hw))
goto err_free;
+ at91sam9260_pmc->chws[PMC_PLLACK] = hw;
+
hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
data->pllb_layout,
data->pllb_characteristics);
if (IS_ERR(hw))
goto err_free;
+ at91sam9260_pmc->chws[PMC_PLLBCK] = hw;
+
parent_names[0] = slck_name;
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);
+ 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;
@@ -428,21 +444,24 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
parent_names[2] = "pllack";
parent_names[3] = "pllbck";
for (i = 0; i < data->num_progck; i++) {
- char *name;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i,
- &at91rm9200_programmable_layout);
+ &at91rm9200_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
+
+ at91sam9260_pmc->pchws[i] = hw;
}
for (i = 0; i < data->num_sck; i++) {
hw = at91_clk_register_system(regmap, data->sck[i].n,
data->sck[i].p,
- data->sck[i].id);
+ data->sck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -452,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;
@@ -460,38 +479,38 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
at91sam9260_pmc->phws[data->pck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9260_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9260_pmc);
return;
err_free:
- pmc_data_free(at91sam9260_pmc);
+ kfree(at91sam9260_pmc);
}
static void __init at91sam9260_pmc_setup(struct device_node *np)
{
at91sam926x_pmc_setup(np, &at91sam9260_data);
}
-CLK_OF_DECLARE_DRIVER(at91sam9260_pmc, "atmel,at91sam9260-pmc",
- at91sam9260_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9260_pmc, "atmel,at91sam9260-pmc", at91sam9260_pmc_setup);
static void __init at91sam9261_pmc_setup(struct device_node *np)
{
at91sam926x_pmc_setup(np, &at91sam9261_data);
}
-CLK_OF_DECLARE_DRIVER(at91sam9261_pmc, "atmel,at91sam9261-pmc",
- at91sam9261_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9261_pmc, "atmel,at91sam9261-pmc", at91sam9261_pmc_setup);
static void __init at91sam9263_pmc_setup(struct device_node *np)
{
at91sam926x_pmc_setup(np, &at91sam9263_data);
}
-CLK_OF_DECLARE_DRIVER(at91sam9263_pmc, "atmel,at91sam9263-pmc",
- at91sam9263_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9263_pmc, "atmel,at91sam9263-pmc", at91sam9263_pmc_setup);
static void __init at91sam9g20_pmc_setup(struct device_node *np)
{
at91sam926x_pmc_setup(np, &at91sam9g20_data);
}
-CLK_OF_DECLARE_DRIVER(at91sam9g20_pmc, "atmel,at91sam9g20-pmc",
- at91sam9g20_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9g20_pmc, "atmel,at91sam9g20-pmc", at91sam9g20_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9g45.c b/drivers/clk/at91/at91sam9g45.c
new file mode 100644
index 0000000000..fedf961393
--- /dev/null
+++ b/drivers/clk/at91/at91sam9g45.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
+#include <mfd/syscon.h>
+#include <linux/slab.h>
+#include <stdio.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#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 },
+};
+
+static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
+
+static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
+
+static const struct clk_range plla_outputs[] = {
+ { .min = 745000000, .max = 800000000 },
+ { .min = 695000000, .max = 750000000 },
+ { .min = 645000000, .max = 700000000 },
+ { .min = 595000000, .max = 650000000 },
+ { .min = 545000000, .max = 600000000 },
+ { .min = 495000000, .max = 555000000 },
+ { .min = 445000000, .max = 500000000 },
+ { .min = 400000000, .max = 450000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+ .input = { .min = 2000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(plla_outputs),
+ .output = plla_outputs,
+ .icpll = plla_icpll,
+ .out = plla_out,
+};
+
+static const struct {
+ char *n;
+ char *p;
+ unsigned long flags;
+ u8 id;
+} at91sam9g45_systemck[] = {
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+};
+
+struct pck {
+ char *n;
+ u8 id;
+};
+
+static const struct pck at91sam9g45_periphck[] = {
+ { .n = "pioA_clk", .id = 2, },
+ { .n = "pioB_clk", .id = 3, },
+ { .n = "pioC_clk", .id = 4, },
+ { .n = "pioDE_clk", .id = 5, },
+ { .n = "trng_clk", .id = 6, },
+ { .n = "usart0_clk", .id = 7, },
+ { .n = "usart1_clk", .id = 8, },
+ { .n = "usart2_clk", .id = 9, },
+ { .n = "usart3_clk", .id = 10, },
+ { .n = "mci0_clk", .id = 11, },
+ { .n = "twi0_clk", .id = 12, },
+ { .n = "twi1_clk", .id = 13, },
+ { .n = "spi0_clk", .id = 14, },
+ { .n = "spi1_clk", .id = 15, },
+ { .n = "ssc0_clk", .id = 16, },
+ { .n = "ssc1_clk", .id = 17, },
+ { .n = "tcb0_clk", .id = 18, },
+ { .n = "pwm_clk", .id = 19, },
+ { .n = "adc_clk", .id = 20, },
+ { .n = "dma0_clk", .id = 21, },
+ { .n = "uhphs_clk", .id = 22, },
+ { .n = "lcd_clk", .id = 23, },
+ { .n = "ac97_clk", .id = 24, },
+ { .n = "macb0_clk", .id = 25, },
+ { .n = "isi_clk", .id = 26, },
+ { .n = "udphs_clk", .id = 27, },
+ { .n = "aestdessha_clk", .id = 28, },
+ { .n = "mci1_clk", .id = 29, },
+ { .n = "vdec_clk", .id = 30, },
+};
+
+static void __init at91sam9g45_pmc_setup(struct device_node *np)
+{
+ const char *slck_name, *mainxtal_name;
+ struct pmc_data *at91sam9g45_pmc;
+ const char *parent_names[6];
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ int i;
+ bool bypass;
+
+ i = of_property_match_string(np, "clock-names", "slow_clk");
+ if (i < 0)
+ return;
+
+ slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = device_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ at91sam9g45_pmc = pmc_data_allocate(PMC_PLLACK + 1,
+ nck(at91sam9g45_systemck),
+ nck(at91sam9g45_periphck), 0, 2);
+ if (!at91sam9g45_pmc)
+ return;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9g45_pmc->chws[PMC_MAIN] = hw;
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ &at91rm9200_pll_layout, &plla_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9g45_pmc->chws[PMC_PLLACK] = hw;
+
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9g45_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91rm9200_master_layout,
+ &mck_characteristics,
+ &at91sam9g45_mck_lock);
+ 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;
+
+ at91sam9g45_pmc->chws[PMC_MCK] = hw;
+
+ parent_names[0] = "plladivck";
+ parent_names[1] = "utmick";
+ hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ parent_names[4] = "masterck_div";
+ for (i = 0; i < 2; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 5, i,
+ &at91sam9g45_programmable_layout,
+ NULL);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9g45_pmc->pchws[i] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91sam9g45_systemck); i++) {
+ hw = at91_clk_register_system(regmap, at91sam9g45_systemck[i].n,
+ at91sam9g45_systemck[i].p,
+ at91sam9g45_systemck[i].id,
+ at91sam9g45_systemck[i].flags);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9g45_pmc->shws[at91sam9g45_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91sam9g45_periphck); i++) {
+ hw = at91_clk_register_peripheral(regmap,
+ at91sam9g45_periphck[i].n,
+ "masterck_div",
+ at91sam9g45_periphck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9g45_pmc->phws[at91sam9g45_periphck[i].id] = hw;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9g45_pmc);
+
+ return;
+
+err_free:
+ kfree(at91sam9g45_pmc);
+}
+/*
+ * The TCB is used as the clocksource so its clock is needed early. This means
+ * this can't be a platform driver.
+ */
+CLK_OF_DECLARE(at91sam9g45_pmc, "atmel,at91sam9g45-pmc", at91sam9g45_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9n12.c b/drivers/clk/at91/at91sam9n12.c
new file mode 100644
index 0000000000..bb075de9fd
--- /dev/null
+++ b/drivers/clk/at91/at91sam9n12.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
+#include <mfd/syscon.h>
+#include <linux/slab.h>
+#include <stdio.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#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 },
+ .have_div3_pres = 1,
+};
+
+static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
+
+static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
+
+static const struct clk_range plla_outputs[] = {
+ { .min = 745000000, .max = 800000000 },
+ { .min = 695000000, .max = 750000000 },
+ { .min = 645000000, .max = 700000000 },
+ { .min = 595000000, .max = 650000000 },
+ { .min = 545000000, .max = 600000000 },
+ { .min = 495000000, .max = 555000000 },
+ { .min = 445000000, .max = 500000000 },
+ { .min = 400000000, .max = 450000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+ .input = { .min = 2000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(plla_outputs),
+ .output = plla_outputs,
+ .icpll = plla_icpll,
+ .out = plla_out,
+};
+
+static u8 pllb_out[] = { 0 };
+
+static const struct clk_range pllb_outputs[] = {
+ { .min = 30000000, .max = 100000000 },
+};
+
+static const struct clk_pll_characteristics pllb_characteristics = {
+ .input = { .min = 2000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(pllb_outputs),
+ .output = pllb_outputs,
+ .out = pllb_out,
+};
+
+static const struct {
+ char *n;
+ char *p;
+ unsigned long flags;
+ u8 id;
+} at91sam9n12_systemck[] = {
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
+ { .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 = {
+ .offset = 0x10c,
+ .cmd = BIT(12),
+ .pid_mask = GENMASK(5, 0),
+ .div_mask = GENMASK(17, 16),
+};
+
+struct pck {
+ char *n;
+ u8 id;
+};
+
+static const struct pck at91sam9n12_periphck[] = {
+ { .n = "pioAB_clk", .id = 2, },
+ { .n = "pioCD_clk", .id = 3, },
+ { .n = "fuse_clk", .id = 4, },
+ { .n = "usart0_clk", .id = 5, },
+ { .n = "usart1_clk", .id = 6, },
+ { .n = "usart2_clk", .id = 7, },
+ { .n = "usart3_clk", .id = 8, },
+ { .n = "twi0_clk", .id = 9, },
+ { .n = "twi1_clk", .id = 10, },
+ { .n = "mci0_clk", .id = 12, },
+ { .n = "spi0_clk", .id = 13, },
+ { .n = "spi1_clk", .id = 14, },
+ { .n = "uart0_clk", .id = 15, },
+ { .n = "uart1_clk", .id = 16, },
+ { .n = "tcb_clk", .id = 17, },
+ { .n = "pwm_clk", .id = 18, },
+ { .n = "adc_clk", .id = 19, },
+ { .n = "dma0_clk", .id = 20, },
+ { .n = "uhphs_clk", .id = 22, },
+ { .n = "udphs_clk", .id = 23, },
+ { .n = "lcdc_clk", .id = 25, },
+ { .n = "sha_clk", .id = 27, },
+ { .n = "ssc0_clk", .id = 28, },
+ { .n = "aes_clk", .id = 29, },
+ { .n = "trng_clk", .id = 30, },
+};
+
+static void __init at91sam9n12_pmc_setup(struct device_node *np)
+{
+ struct clk_range range = CLK_RANGE(0, 0);
+ const char *slck_name, *mainxtal_name;
+ struct pmc_data *at91sam9n12_pmc;
+ const char *parent_names[6];
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ int i;
+ bool bypass;
+
+ i = of_property_match_string(np, "clock-names", "slow_clk");
+ if (i < 0)
+ return;
+
+ slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = device_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ at91sam9n12_pmc = pmc_data_allocate(PMC_PLLBCK + 1,
+ nck(at91sam9n12_systemck), 31, 0, 2);
+ if (!at91sam9n12_pmc)
+ return;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9n12_pmc->chws[PMC_MAIN] = hw;
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ &at91rm9200_pll_layout, &plla_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9n12_pmc->chws[PMC_PLLACK] = hw;
+
+ hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
+ &at91rm9200_pll_layout, &pllb_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9n12_pmc->chws[PMC_PLLBCK] = hw;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "pllbck";
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics,
+ &at91sam9n12_mck_lock);
+ 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;
+
+ at91sam9n12_pmc->chws[PMC_MCK] = hw;
+
+ hw = at91sam9n12_clk_register_usb(regmap, "usbck", "pllbck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "pllbck";
+ parent_names[4] = "masterck_div";
+ for (i = 0; i < 2; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 5, i,
+ &at91sam9x5_programmable_layout,
+ NULL);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9n12_pmc->pchws[i] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91sam9n12_systemck); i++) {
+ hw = at91_clk_register_system(regmap, at91sam9n12_systemck[i].n,
+ at91sam9n12_systemck[i].p,
+ at91sam9n12_systemck[i].id,
+ at91sam9n12_systemck[i].flags);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9n12_pmc->shws[at91sam9n12_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91sam9n12_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ &at91sam9n12_pcr_layout,
+ at91sam9n12_periphck[i].n,
+ "masterck_div",
+ at91sam9n12_periphck[i].id,
+ &range, INT_MIN, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9n12_pmc->phws[at91sam9n12_periphck[i].id] = hw;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9n12_pmc);
+
+ return;
+
+err_free:
+ kfree(at91sam9n12_pmc);
+}
+/*
+ * The TCB is used as the clocksource so its clock is needed early. This means
+ * this can't be a platform driver.
+ */
+CLK_OF_DECLARE(at91sam9n12_pmc, "atmel,at91sam9n12-pmc", at91sam9n12_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9rl.c b/drivers/clk/at91/at91sam9rl.c
index ff47f94a8d..95b02d86d5 100644
--- a/drivers/clk/at91/at91sam9rl.c
+++ b/drivers/clk/at91/at91sam9rl.c
@@ -1,18 +1,15 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#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 },
@@ -75,7 +72,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
struct pmc_data *at91sam9rl_pmc;
const char *parent_names[6];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
i = of_property_match_string(np, "clock-names", "slow_clk");
@@ -93,9 +90,9 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- at91sam9rl_pmc = pmc_data_allocate(PMC_MAIN + 1,
+ at91sam9rl_pmc = pmc_data_allocate(PMC_PLLACK + 1,
nck(at91sam9rl_systemck),
- nck(at91sam9rl_periphck), 0);
+ nck(at91sam9rl_periphck), 0, 2);
if (!at91sam9rl_pmc)
return;
@@ -111,6 +108,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
if (IS_ERR(hw))
goto err_free;
+ at91sam9rl_pmc->chws[PMC_PLLACK] = hw;
+
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
if (IS_ERR(hw))
goto err_free;
@@ -121,9 +120,19 @@ 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);
+ 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;
@@ -133,23 +142,26 @@ 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;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91rm9200_programmable_layout);
+ &at91rm9200_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
+
+ at91sam9rl_pmc->pchws[i] = hw;
}
for (i = 0; i < ARRAY_SIZE(at91sam9rl_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9rl_systemck[i].n,
at91sam9rl_systemck[i].p,
- at91sam9rl_systemck[i].id);
+ at91sam9rl_systemck[i].id, 0);
if (IS_ERR(hw))
goto err_free;
@@ -159,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;
@@ -167,11 +179,12 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
at91sam9rl_pmc->phws[at91sam9rl_periphck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9rl_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9rl_pmc);
return;
err_free:
- pmc_data_free(at91sam9rl_pmc);
+ kfree(at91sam9rl_pmc);
}
-CLK_OF_DECLARE_DRIVER(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c
index baa71aa105..f4dc7ceeea 100644
--- a/drivers/clk/at91/at91sam9x5.c
+++ b/drivers/clk/at91/at91sam9x5.c
@@ -1,18 +1,15 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#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 },
@@ -45,9 +42,14 @@ static const struct clk_pll_characteristics plla_characteristics = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} at91sam9x5_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .n = "smdck", .p = "smdclk", .id = 4 },
{ .n = "uhpck", .p = "usbck", .id = 6 },
{ .n = "udpck", .p = "usbck", .id = 7 },
@@ -137,7 +139,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
struct pmc_data *at91sam9x5_pmc;
const char *parent_names[6];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -156,8 +158,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
if (IS_ERR(regmap))
return;
- at91sam9x5_pmc = pmc_data_allocate(PMC_MAIN + 1,
- nck(at91sam9x5_systemck), 31, 0);
+ at91sam9x5_pmc = pmc_data_allocate(PMC_PLLACK + 1,
+ nck(at91sam9x5_systemck), 31, 0, 2);
if (!at91sam9x5_pmc)
return;
@@ -190,6 +192,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
if (IS_ERR(hw))
goto err_free;
+ at91sam9x5_pmc->chws[PMC_PLLACK] = hw;
+
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
if (IS_ERR(hw))
goto err_free;
@@ -200,9 +204,18 @@ 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);
+ 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;
@@ -222,23 +235,27 @@ 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;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
+
+ at91sam9x5_pmc->pchws[i] = hw;
}
for (i = 0; i < ARRAY_SIZE(at91sam9x5_systemck); i++) {
hw = at91_clk_register_system(regmap, at91sam9x5_systemck[i].n,
at91sam9x5_systemck[i].p,
- at91sam9x5_systemck[i].id);
+ at91sam9x5_systemck[i].id,
+ at91sam9x5_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -246,7 +263,8 @@ 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, 0);
if (IS_ERR(hw))
goto err_free;
@@ -254,12 +272,12 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
}
for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ 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);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
@@ -267,57 +285,57 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
}
for (i = 0; extra_pcks[i].id; i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ 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);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
at91sam9x5_pmc->phws[extra_pcks[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc);
return;
err_free:
- pmc_data_free(at91sam9x5_pmc);
+ kfree(at91sam9x5_pmc);
}
static void __init at91sam9g15_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9g15_periphck, true);
}
-CLK_OF_DECLARE_DRIVER(at91sam9g15_pmc, "atmel,at91sam9g15-pmc",
- at91sam9g15_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9g15_pmc, "atmel,at91sam9g15-pmc", at91sam9g15_pmc_setup);
static void __init at91sam9g25_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9g25_periphck, false);
}
-CLK_OF_DECLARE_DRIVER(at91sam9g25_pmc, "atmel,at91sam9g25-pmc",
- at91sam9g25_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9g25_pmc, "atmel,at91sam9g25-pmc", at91sam9g25_pmc_setup);
static void __init at91sam9g35_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9g35_periphck, true);
}
-CLK_OF_DECLARE_DRIVER(at91sam9g35_pmc, "atmel,at91sam9g35-pmc",
- at91sam9g35_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9g35_pmc, "atmel,at91sam9g35-pmc", at91sam9g35_pmc_setup);
static void __init at91sam9x25_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9x25_periphck, false);
}
-CLK_OF_DECLARE_DRIVER(at91sam9x25_pmc, "atmel,at91sam9x25-pmc",
- at91sam9x25_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9x25_pmc, "atmel,at91sam9x25-pmc", at91sam9x25_pmc_setup);
static void __init at91sam9x35_pmc_setup(struct device_node *np)
{
at91sam9x5_pmc_setup(np, at91sam9x35_periphck, true);
}
-CLK_OF_DECLARE_DRIVER(at91sam9x35_pmc, "atmel,at91sam9x35-pmc",
- at91sam9x35_pmc_setup);
+
+CLK_OF_DECLARE(at91sam9x35_pmc, "atmel,at91sam9x35-pmc", at91sam9x35_pmc_setup);
diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
index 47bff32fe8..71976567ea 100644
--- a/drivers/clk/at91/clk-audio-pll.c
+++ b/drivers/clk/at91/clk-audio-pll.c
@@ -30,14 +30,14 @@
* parent - fixed parent. No clk_set_parent support
*/
-#include <common.h>
-#include <clock.h>
-#include <of.h>
-#include <linux/list.h>
#include <linux/clk.h>
+#include <linux/printk.h>
+#include <linux/clk-provider.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
#include "pmc.h"
@@ -57,35 +57,32 @@
#define AUDIO_PLL_FOUT_MAX 700000000UL
struct clk_audio_frac {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
u32 fracr;
u8 nd;
- const char *parent_name;
};
struct clk_audio_pad {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
u8 qdaudio;
u8 div;
- const char *parent_name;
};
struct clk_audio_pmc {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
u8 qdpmc;
- const char *parent_name;
};
-#define to_clk_audio_frac(clk) container_of(clk, struct clk_audio_frac, clk)
-#define to_clk_audio_pad(clk) container_of(clk, struct clk_audio_pad, clk)
-#define to_clk_audio_pmc(clk) container_of(clk, struct clk_audio_pmc, clk)
+#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
+#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
+#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
-static int clk_audio_pll_frac_enable(struct clk *clk)
+static int clk_audio_pll_frac_enable(struct clk_hw *hw)
{
- struct clk_audio_frac *frac = to_clk_audio_frac(clk);
+ struct clk_audio_frac *frac = to_clk_audio_frac(hw);
regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0,
AT91_PMC_AUDIO_PLL_RESETN, 0);
@@ -108,9 +105,9 @@ static int clk_audio_pll_frac_enable(struct clk *clk)
return 0;
}
-static int clk_audio_pll_pad_enable(struct clk *clk)
+static int clk_audio_pll_pad_enable(struct clk_hw *hw)
{
- struct clk_audio_pad *apad_ck = to_clk_audio_pad(clk);
+ struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
AT91_PMC_AUDIO_PLL_QDPAD_MASK,
@@ -121,9 +118,9 @@ static int clk_audio_pll_pad_enable(struct clk *clk)
return 0;
}
-static int clk_audio_pll_pmc_enable(struct clk *clk)
+static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
{
- struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(clk);
+ struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
AT91_PMC_AUDIO_PLL_PMCEN |
@@ -133,9 +130,9 @@ static int clk_audio_pll_pmc_enable(struct clk *clk)
return 0;
}
-static void clk_audio_pll_frac_disable(struct clk *clk)
+static void clk_audio_pll_frac_disable(struct clk_hw *hw)
{
- struct clk_audio_frac *frac = to_clk_audio_frac(clk);
+ struct clk_audio_frac *frac = to_clk_audio_frac(hw);
regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0,
AT91_PMC_AUDIO_PLL_PLLEN, 0);
@@ -144,17 +141,17 @@ static void clk_audio_pll_frac_disable(struct clk *clk)
AT91_PMC_AUDIO_PLL_RESETN, 0);
}
-static void clk_audio_pll_pad_disable(struct clk *clk)
+static void clk_audio_pll_pad_disable(struct clk_hw *hw)
{
- struct clk_audio_pad *apad_ck = to_clk_audio_pad(clk);
+ struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
AT91_PMC_AUDIO_PLL_PADEN, 0);
}
-static void clk_audio_pll_pmc_disable(struct clk *clk)
+static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
{
- struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(clk);
+ struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
AT91_PMC_AUDIO_PLL_PMCEN, 0);
@@ -174,10 +171,10 @@ static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
return parent_rate * (nd + 1) + fr;
}
-static unsigned long clk_audio_pll_frac_recalc_rate(struct clk *clk,
+static unsigned long clk_audio_pll_frac_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_audio_frac *frac = to_clk_audio_frac(clk);
+ struct clk_audio_frac *frac = to_clk_audio_frac(hw);
unsigned long fout;
fout = clk_audio_pll_fout(parent_rate, frac->nd, frac->fracr);
@@ -188,10 +185,10 @@ static unsigned long clk_audio_pll_frac_recalc_rate(struct clk *clk,
return fout;
}
-static unsigned long clk_audio_pll_pad_recalc_rate(struct clk *clk,
+static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_audio_pad *apad_ck = to_clk_audio_pad(clk);
+ struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
unsigned long apad_rate = 0;
if (apad_ck->qdaudio && apad_ck->div)
@@ -203,10 +200,10 @@ static unsigned long clk_audio_pll_pad_recalc_rate(struct clk *clk,
return apad_rate;
}
-static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk *clk,
+static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(clk);
+ struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
unsigned long apmc_rate = 0;
apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
@@ -245,10 +242,10 @@ static int clk_audio_pll_frac_compute_frac(unsigned long rate,
return 0;
}
-static long clk_audio_pll_pad_round_rate(struct clk *clk, unsigned long rate,
+static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
- struct clk *pclk = clk_get_parent(clk);
+ struct clk_hw *pclk = clk_hw_get_parent(hw);
long best_rate = -EINVAL;
unsigned long best_parent_rate;
unsigned long tmp_qd;
@@ -278,7 +275,7 @@ static long clk_audio_pll_pad_round_rate(struct clk *clk, unsigned long rate,
if (div == 2 && tmp_qd % 3 == 0)
continue;
- best_parent_rate = clk_round_rate(pclk,
+ best_parent_rate = clk_hw_round_rate(pclk,
rate * tmp_qd * div);
tmp_rate = best_parent_rate / (div * tmp_qd);
tmp_diff = abs(rate - tmp_rate);
@@ -296,10 +293,10 @@ static long clk_audio_pll_pad_round_rate(struct clk *clk, unsigned long rate,
return best_rate;
}
-static long clk_audio_pll_pmc_round_rate(struct clk *clk, unsigned long rate,
+static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
- struct clk *pclk = clk_get_parent(clk);
+ struct clk_hw *pclk = clk_hw_get_parent(hw);
long best_rate = -EINVAL;
unsigned long best_parent_rate = 0;
u32 tmp_qd = 0, div;
@@ -313,10 +310,10 @@ static long clk_audio_pll_pmc_round_rate(struct clk *clk, unsigned long rate,
if (!rate)
return 0;
- best_parent_rate = clk_round_rate(pclk, 1);
+ best_parent_rate = clk_round_rate(&pclk->clk, 1);
div = max(best_parent_rate / rate, 1UL);
for (; div <= AUDIO_PLL_QDPMC_MAX; div++) {
- best_parent_rate = clk_round_rate(pclk, rate * div);
+ best_parent_rate = clk_round_rate(&pclk->clk, rate * div);
tmp_rate = best_parent_rate / div;
tmp_diff = abs(rate - tmp_rate);
@@ -336,10 +333,10 @@ static long clk_audio_pll_pmc_round_rate(struct clk *clk, unsigned long rate,
return best_rate;
}
-static int clk_audio_pll_frac_set_rate(struct clk *clk, unsigned long rate,
+static int clk_audio_pll_frac_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct clk_audio_frac *frac = to_clk_audio_frac(clk);
+ struct clk_audio_frac *frac = to_clk_audio_frac(hw);
unsigned long fracr, nd;
int ret;
@@ -359,10 +356,10 @@ static int clk_audio_pll_frac_set_rate(struct clk *clk, unsigned long rate,
return 0;
}
-static int clk_audio_pll_pad_set_rate(struct clk *clk, unsigned long rate,
+static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct clk_audio_pad *apad_ck = to_clk_audio_pad(clk);
+ struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
u8 tmp_div;
pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
@@ -383,10 +380,10 @@ static int clk_audio_pll_pad_set_rate(struct clk *clk, unsigned long rate,
return 0;
}
-static int clk_audio_pll_pmc_set_rate(struct clk *clk, unsigned long rate,
+static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(clk);
+ struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
if (!rate)
return -EINVAL;
@@ -422,91 +419,94 @@ static const struct clk_ops audio_pll_pmc_ops = {
.set_rate = clk_audio_pll_pmc_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_frac(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_audio_frac *frac_ck;
+ struct clk_init_data init = {};
int ret;
frac_ck = kzalloc(sizeof(*frac_ck), GFP_KERNEL);
if (!frac_ck)
return ERR_PTR(-ENOMEM);
- frac_ck->clk.name = name;
- frac_ck->clk.ops = &audio_pll_frac_ops;
- frac_ck->parent_name = parent_name;
- frac_ck->clk.parent_names = &frac_ck->parent_name;
- frac_ck->clk.num_parents = 1;
- /* frac_ck->clk.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = &audio_pll_frac_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE;
+ frac_ck->hw.init = &init;
frac_ck->regmap = regmap;
- ret = clk_register(&frac_ck->clk);
+ ret = clk_hw_register(NULL, &frac_ck->hw);
if (ret) {
kfree(frac_ck);
return ERR_PTR(ret);
}
- return &frac_ck->clk;
+ return &frac_ck->hw;
}
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_pad(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_audio_pad *apad_ck;
+ struct clk_init_data init;
int ret;
apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
if (!apad_ck)
return ERR_PTR(-ENOMEM);
- apad_ck->clk.name = name;
- apad_ck->clk.ops = &audio_pll_pad_ops;
- apad_ck->parent_name = parent_name;
- apad_ck->clk.parent_names = &apad_ck->parent_name;
- apad_ck->clk.num_parents = 1;
- /* apad_ck->clk.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT; */
+ init.name = name;
+ init.ops = &audio_pll_pad_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+ apad_ck->hw.init = &init;
apad_ck->regmap = regmap;
- ret = clk_register(&apad_ck->clk);
+ ret = clk_hw_register(NULL, &apad_ck->hw);
if (ret) {
kfree(apad_ck);
return ERR_PTR(ret);
}
- return &apad_ck->clk;
+ return &apad_ck->hw;
}
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_pmc(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_audio_pmc *apmc_ck;
+ struct clk_init_data init;
int ret;
apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
if (!apmc_ck)
return ERR_PTR(-ENOMEM);
- apmc_ck->clk.name = name;
- apmc_ck->clk.ops = &audio_pll_pmc_ops;
- apmc_ck->parent_name = parent_name;
- apmc_ck->clk.parent_names = &apmc_ck->parent_name;
- apmc_ck->clk.num_parents = 1;
- /* apmc_ck.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT; */
+ init.name = name;
+ init.ops = &audio_pll_pmc_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+ apmc_ck->hw.init = &init;
apmc_ck->regmap = regmap;
- ret = clk_register(&apmc_ck->clk);
+ ret = clk_hw_register(NULL, &apmc_ck->hw);
if (ret) {
kfree(apmc_ck);
return ERR_PTR(ret);
}
- return &apmc_ck->clk;
+ return &apmc_ck->hw;
}
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 56b800facb..e59cff2bdf 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -6,80 +6,99 @@
* Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/bitfield.h>
+#include <linux/printk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
-#include <linux/bitfield.h>
+#include <linux/regmap.h>
#include "pmc.h"
#define GENERATED_MAX_DIV 255
-#define GCK_INDEX_DT_AUDIO_PLL 5
-
struct clk_generated {
- struct clk hw;
+ struct clk_hw hw;
struct regmap *regmap;
struct clk_range range;
+ spinlock_t *lock;
+ u32 *mux_table;
u32 id;
u32 gckdiv;
const struct clk_pcr_layout *layout;
+ struct at91_clk_pms pms;
u8 parent_id;
- bool audio_pll_allowed;
+ int chg_pid;
};
#define to_clk_generated(hw) \
container_of(hw, struct clk_generated, hw)
-static int clk_generated_enable(struct clk *hw)
+static int clk_generated_set(struct clk_generated *gck, int status)
{
- struct clk_generated *gck = to_clk_generated(hw);
-
- pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
- __func__, gck->gckdiv, gck->parent_id);
+ unsigned long flags;
+ unsigned int enable = status ? AT91_PMC_PCR_GCKEN : 0;
+ spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_update_bits(gck->regmap, gck->layout->offset,
AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask |
- gck->layout->cmd | AT91_PMC_PCR_GCKEN,
+ gck->layout->cmd | enable,
field_prep(gck->layout->gckcss_mask, gck->parent_id) |
gck->layout->cmd |
FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) |
- AT91_PMC_PCR_GCKEN);
+ enable);
+ spin_unlock_irqrestore(gck->lock, flags);
+
return 0;
}
-static void clk_generated_disable(struct clk *hw)
+static int clk_generated_enable(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
+ pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
+ __func__, gck->gckdiv, gck->parent_id);
+
+ clk_generated_set(gck, 1);
+
+ return 0;
+}
+
+static void clk_generated_disable(struct clk_hw *hw)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_update_bits(gck->regmap, gck->layout->offset,
gck->layout->cmd | AT91_PMC_PCR_GCKEN,
gck->layout->cmd);
+ spin_unlock_irqrestore(gck->lock, flags);
}
-static int clk_generated_is_enabled(struct clk *hw)
+static int clk_generated_is_enabled(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
+ unsigned long flags;
unsigned int status;
+ spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_read(gck->regmap, gck->layout->offset, &status);
+ spin_unlock_irqrestore(gck->lock, flags);
- return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
+ return !!(status & AT91_PMC_PCR_GCKEN);
}
static unsigned long
-clk_generated_recalc_rate(struct clk *hw,
+clk_generated_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_generated *gck = to_clk_generated(hw);
@@ -88,18 +107,22 @@ clk_generated_recalc_rate(struct clk *hw,
}
/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
-static int clk_generated_set_parent(struct clk *hw, u8 index)
+static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_generated *gck = to_clk_generated(hw);
- if (index >= clk_get_num_parents(hw))
+ if (index >= clk_hw_get_num_parents(hw))
return -EINVAL;
- gck->parent_id = index;
+ if (gck->mux_table)
+ gck->parent_id = clk_mux_index_to_val(gck->mux_table, 0, index);
+ else
+ gck->parent_id = index;
+
return 0;
}
-static int clk_generated_get_parent(struct clk *hw)
+static int clk_generated_get_parent(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
@@ -107,7 +130,7 @@ static int clk_generated_get_parent(struct clk *hw)
}
/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
-static int clk_generated_set_rate(struct clk *hw,
+static int clk_generated_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
@@ -150,53 +173,58 @@ static const struct clk_ops generated_ops = {
static void clk_generated_startup(struct clk_generated *gck)
{
u32 tmp;
+ unsigned long flags;
+ spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_read(gck->regmap, gck->layout->offset, &tmp);
+ spin_unlock_irqrestore(gck->lock, flags);
gck->parent_id = field_get(gck->layout->gckcss_mask, tmp);
gck->gckdiv = FIELD_GET(AT91_PMC_PCR_GCKDIV_MASK, tmp);
}
-struct clk * __init
-at91_clk_register_generated(struct regmap *regmap,
+struct clk_hw * __init
+at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u8 num_parents, u8 id, bool pll_audio,
- const struct clk_range *range)
+ u32 *mux_table, u8 num_parents, u8 id,
+ const struct clk_range *range,
+ int chg_pid)
{
- size_t parents_array_size;
struct clk_generated *gck;
- struct clk *hw;
+ struct clk_init_data init;
+ struct clk_hw *hw;
int ret;
gck = kzalloc(sizeof(*gck), GFP_KERNEL);
if (!gck)
return ERR_PTR(-ENOMEM);
- gck->id = id;
- gck->hw.name = name;
- gck->hw.ops = &generated_ops;
+ init.name = name;
+ init.ops = &generated_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+ if (chg_pid >= 0)
+ init.flags |= CLK_SET_RATE_PARENT;
- parents_array_size = num_parents * sizeof(gck->hw.parent_names[0]);
- gck->hw.parent_names = xmemdup(parent_names, parents_array_size);
- gck->hw.num_parents = num_parents;
-
- /* gck->hw.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | CLK_SET_PARENT; */
+ gck->id = id;
+ gck->hw.init = &init;
gck->regmap = regmap;
+ gck->lock = lock;
gck->range = *range;
- gck->audio_pll_allowed = pll_audio;
+ gck->chg_pid = chg_pid;
gck->layout = layout;
+ gck->mux_table = mux_table;
clk_generated_startup(gck);
hw = &gck->hw;
- ret = clk_register(&gck->hw);
+ ret = clk_hw_register(NULL, &gck->hw);
if (ret) {
kfree(gck);
hw = ERR_PTR(ret);
- } else {
- pmc_register_id(id);
}
return hw;
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
index 6052886cca..e5b98692a9 100644
--- a/drivers/clk/at91/clk-h32mx.c
+++ b/drivers/clk/at91/clk-h32mx.c
@@ -7,27 +7,26 @@
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
-#include <regmap.h>
-
+#include <of.h>
+#include <linux/regmap.h>
+#include <mfd/syscon.h>
+#include <linux/printk.h>
#include "pmc.h"
#define H32MX_MAX_FREQ 90000000
struct clk_sama5d4_h32mx {
- struct clk hw;
+ struct clk_hw hw;
struct regmap *regmap;
- const char *parent;
};
#define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw)
-static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk *hw,
+static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
@@ -42,7 +41,7 @@ static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk *hw,
return parent_rate;
}
-static long clk_sama5d4_h32mx_round_rate(struct clk *hw, unsigned long rate,
+static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long div;
@@ -59,7 +58,7 @@ static long clk_sama5d4_h32mx_round_rate(struct clk *hw, unsigned long rate,
return *parent_rate;
}
-static int clk_sama5d4_h32mx_set_rate(struct clk *hw, unsigned long rate,
+static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
@@ -83,27 +82,28 @@ static const struct clk_ops h32mx_ops = {
.set_rate = clk_sama5d4_h32mx_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_h32mx(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_sama5d4_h32mx *h32mxclk;
+ struct clk_init_data init;
int ret;
h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL);
if (!h32mxclk)
return ERR_PTR(-ENOMEM);
- h32mxclk->parent = parent_name;
- h32mxclk->hw.name = name;
- h32mxclk->hw.ops = &h32mx_ops;
- h32mxclk->hw.parent_names = &h32mxclk->parent;
- h32mxclk->hw.num_parents = 1;
- /* h32mxclk.hw.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = &h32mx_ops;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.flags = CLK_SET_RATE_GATE;
+ h32mxclk->hw.init = &init;
h32mxclk->regmap = regmap;
- ret = clk_register(&h32mxclk->hw);
+ ret = clk_hw_register(NULL, &h32mxclk->hw);
if (ret) {
kfree(h32mxclk);
return ERR_PTR(ret);
diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c
index 1418ec8662..71ef2e6386 100644
--- a/drivers/clk/at91/clk-i2s-mux.c
+++ b/drivers/clk/at91/clk-i2s-mux.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018 Microchip Technology Inc,
* Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
@@ -6,33 +6,27 @@
*
*/
-#include <common.h>
-#include <clock.h>
+#include <linux/clk-provider.h>
#include <of.h>
-#include <linux/list.h>
-#include <linux/clk.h>
-#include <linux/clk/at91_pmc.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
#include <soc/at91/atmel-sfr.h>
#include "pmc.h"
-#define I2S_MUX_SOURCE_MAX 2
-
struct clk_i2s_mux {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
u8 bus_id;
- const char *parent_names[I2S_MUX_SOURCE_MAX];
};
-#define to_clk_i2s_mux(clk) container_of(clk, struct clk_i2s_mux, clk)
+#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw)
-static int clk_i2s_mux_get_parent(struct clk *clk)
+static int clk_i2s_mux_get_parent(struct clk_hw *hw)
{
- struct clk_i2s_mux *mux = to_clk_i2s_mux(clk);
+ struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
u32 val;
regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val);
@@ -40,26 +34,25 @@ static int clk_i2s_mux_get_parent(struct clk *clk)
return (val & BIT(mux->bus_id)) >> mux->bus_id;
}
-static int clk_i2s_mux_set_parent(struct clk *clk, u8 index)
+static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index)
{
- struct clk_i2s_mux *mux = to_clk_i2s_mux(clk);
+ struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL,
BIT(mux->bus_id), index << mux->bus_id);
}
static const struct clk_ops clk_i2s_mux_ops = {
- .set_rate = clk_parent_set_rate,
- .round_rate = clk_parent_round_rate,
.get_parent = clk_i2s_mux_get_parent,
.set_parent = clk_i2s_mux_set_parent,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
const char * const *parent_names,
unsigned int num_parents, u8 bus_id)
{
+ struct clk_init_data init = {};
struct clk_i2s_mux *i2s_ck;
int ret;
@@ -67,21 +60,20 @@ at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
if (!i2s_ck)
return ERR_PTR(-ENOMEM);
- i2s_ck->clk.name = name;
- i2s_ck->clk.ops = &clk_i2s_mux_ops;
- memcpy(i2s_ck->parent_names, parent_names,
- num_parents * sizeof(i2s_ck->parent_names[0]));
- i2s_ck->clk.parent_names = &i2s_ck->parent_names[0];
- i2s_ck->clk.num_parents = num_parents;
+ init.name = name;
+ init.ops = &clk_i2s_mux_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ i2s_ck->hw.init = &init;
i2s_ck->bus_id = bus_id;
i2s_ck->regmap = regmap;
- ret = clk_register(&i2s_ck->clk);
+ ret = clk_hw_register(NULL, &i2s_ck->hw);
if (ret) {
kfree(i2s_ck);
return ERR_PTR(ret);
}
- return &i2s_ck->clk;
+ return &i2s_ck->hw;
}
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 08abb1673b..a1dd327b56 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -2,21 +2,22 @@
/*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <clock.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/printk.h>
#include "pmc.h"
#define SLOW_CLOCK_FREQ 32768
#define MAINF_DIV 16
-#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * SECOND) / \
+#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * USEC_PER_SEC) / \
SLOW_CLOCK_FREQ)
-#define MAINF_LOOP_MIN_WAIT (SECOND / SLOW_CLOCK_FREQ)
+#define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ)
#define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT
#define MOR_KEY_MASK (0xff << 16)
@@ -26,36 +27,38 @@
AT91_PMC_OSCBYPASS)) ? 1 : 0)
struct clk_main_osc {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
- const char *parent;
+ struct at91_clk_pms pms;
};
-#define to_clk_main_osc(clk) container_of(clk, struct clk_main_osc, clk)
+#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)
struct clk_main_rc_osc {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
unsigned long frequency;
+ unsigned long accuracy;
+ struct at91_clk_pms pms;
};
-#define to_clk_main_rc_osc(clk) container_of(clk, struct clk_main_rc_osc, clk)
+#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw)
struct clk_rm9200_main {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
- const char *parent;
};
-#define to_clk_rm9200_main(clk) container_of(clk, struct clk_rm9200_main, clk)
+#define to_clk_rm9200_main(hw) container_of(hw, struct clk_rm9200_main, hw)
struct clk_sam9x5_main {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
+ struct at91_clk_pms pms;
u8 parent;
};
-#define to_clk_sam9x5_main(clk) container_of(clk, struct clk_sam9x5_main, clk)
+#define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw)
static inline bool clk_main_osc_ready(struct regmap *regmap)
{
@@ -66,9 +69,9 @@ static inline bool clk_main_osc_ready(struct regmap *regmap)
return status & AT91_PMC_MOSCS;
}
-static int clk_main_osc_enable(struct clk *clk)
+static int clk_main_osc_prepare(struct clk_hw *hw)
{
- struct clk_main_osc *osc = to_clk_main_osc(clk);
+ struct clk_main_osc *osc = to_clk_main_osc(hw);
struct regmap *regmap = osc->regmap;
u32 tmp;
@@ -84,14 +87,14 @@ static int clk_main_osc_enable(struct clk *clk)
}
while (!clk_main_osc_ready(regmap))
- barrier();
+ cpu_relax();
return 0;
}
-static void clk_main_osc_disable(struct clk *clk)
+static void clk_main_osc_unprepare(struct clk_hw *hw)
{
- struct clk_main_osc *osc = to_clk_main_osc(clk);
+ struct clk_main_osc *osc = to_clk_main_osc(hw);
struct regmap *regmap = osc->regmap;
u32 tmp;
@@ -106,9 +109,9 @@ static void clk_main_osc_disable(struct clk *clk)
regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY);
}
-static int clk_main_osc_is_enabled(struct clk *clk)
+static int clk_main_osc_is_prepared(struct clk_hw *hw)
{
- struct clk_main_osc *osc = to_clk_main_osc(clk);
+ struct clk_main_osc *osc = to_clk_main_osc(hw);
struct regmap *regmap = osc->regmap;
u32 tmp, status;
@@ -122,45 +125,52 @@ static int clk_main_osc_is_enabled(struct clk *clk)
}
static const struct clk_ops main_osc_ops = {
- .enable = clk_main_osc_enable,
- .disable = clk_main_osc_disable,
- .is_enabled = clk_main_osc_is_enabled,
+ .enable = clk_main_osc_prepare,
+ .disable = clk_main_osc_unprepare,
+ .is_enabled = clk_main_osc_is_prepared,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_main_osc(struct regmap *regmap,
const char *name,
const char *parent_name,
bool bypass)
{
struct clk_main_osc *osc;
+ struct clk_init_data init;
+ struct clk_hw *hw;
int ret;
if (!name || !parent_name)
return ERR_PTR(-EINVAL);
- osc = xzalloc(sizeof(*osc));
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
- osc->parent = parent_name;
- osc->clk.name = name;
- osc->clk.ops = &main_osc_ops;
- osc->clk.parent_names = &osc->parent;
- osc->clk.num_parents = 1;
+ init.name = name;
+ init.ops = &main_osc_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_IGNORE_UNUSED;
+
+ osc->hw.init = &init;
osc->regmap = regmap;
if (bypass)
- regmap_write_bits(regmap,
- AT91_CKGR_MOR, MOR_KEY_MASK |
- AT91_PMC_MOSCEN,
- AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
+ regmap_update_bits(regmap,
+ AT91_CKGR_MOR, MOR_KEY_MASK |
+ AT91_PMC_OSCBYPASS,
+ AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
- ret = clk_register(&osc->clk);
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
if (ret) {
- free(osc);
- return ERR_PTR(ret);
+ kfree(osc);
+ hw = ERR_PTR(ret);
}
- return &osc->clk;
+ return hw;
}
static bool clk_main_rc_osc_ready(struct regmap *regmap)
@@ -169,31 +179,31 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status);
- return status & AT91_PMC_MOSCRCS;
+ return !!(status & AT91_PMC_MOSCRCS);
}
-static int clk_main_rc_osc_enable(struct clk *clk)
+static int clk_main_rc_osc_prepare(struct clk_hw *hw)
{
- struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
struct regmap *regmap = osc->regmap;
unsigned int mor;
regmap_read(regmap, AT91_CKGR_MOR, &mor);
if (!(mor & AT91_PMC_MOSCRCEN))
- regmap_write_bits(regmap, AT91_CKGR_MOR,
- MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
- AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
+ regmap_update_bits(regmap, AT91_CKGR_MOR,
+ MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
+ AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
while (!clk_main_rc_osc_ready(regmap))
- barrier();
+ cpu_relax();
return 0;
}
-static void clk_main_rc_osc_disable(struct clk *clk)
+static void clk_main_rc_osc_unprepare(struct clk_hw *hw)
{
- struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
struct regmap *regmap = osc->regmap;
unsigned int mor;
@@ -202,13 +212,13 @@ static void clk_main_rc_osc_disable(struct clk *clk)
if (!(mor & AT91_PMC_MOSCRCEN))
return;
- regmap_write_bits(regmap, AT91_CKGR_MOR,
- MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY);
+ regmap_update_bits(regmap, AT91_CKGR_MOR,
+ MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY);
}
-static int clk_main_rc_osc_is_enabled(struct clk *clk)
+static int clk_main_rc_osc_is_prepared(struct clk_hw *hw)
{
- struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
struct regmap *regmap = osc->regmap;
unsigned int mor, status;
@@ -218,61 +228,71 @@ static int clk_main_rc_osc_is_enabled(struct clk *clk)
return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS);
}
-static unsigned long clk_main_rc_osc_recalc_rate(struct clk *clk,
+static unsigned long clk_main_rc_osc_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
return osc->frequency;
}
static const struct clk_ops main_rc_osc_ops = {
- .enable = clk_main_rc_osc_enable,
- .disable = clk_main_rc_osc_disable,
- .is_enabled = clk_main_rc_osc_is_enabled,
+ .enable = clk_main_rc_osc_prepare,
+ .disable = clk_main_rc_osc_unprepare,
+ .is_enabled = clk_main_rc_osc_is_prepared,
.recalc_rate = clk_main_rc_osc_recalc_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_main_rc_osc(struct regmap *regmap,
const char *name,
u32 frequency, u32 accuracy)
{
- int ret;
struct clk_main_rc_osc *osc;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !frequency)
return ERR_PTR(-EINVAL);
- osc = xzalloc(sizeof(*osc));
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
- osc->clk.name = name;
- osc->clk.ops = &main_rc_osc_ops;
- osc->clk.parent_names = NULL;
- osc->clk.num_parents = 0;
+ init.name = name;
+ init.ops = &main_rc_osc_ops;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ init.flags = CLK_IGNORE_UNUSED;
+ osc->hw.init = &init;
osc->regmap = regmap;
osc->frequency = frequency;
+ osc->accuracy = accuracy;
- ret = clk_register(&osc->clk);
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(osc);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &osc->clk;
+ return hw;
}
static int clk_main_probe_frequency(struct regmap *regmap)
{
+ u64 start_time;
unsigned int mcfr;
- uint64_t start = get_time_ns();
+ start_time = get_time_ns();
do {
regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
if (mcfr & AT91_PMC_MAINRDY)
return 0;
- } while (!is_timeout(start, MAINFRDY_TIMEOUT * USECOND));
+ udelay(MAINF_LOOP_MIN_WAIT);
+ } while (!is_timeout(start_time, MAINFRDY_TIMEOUT * NSEC_PER_USEC));
return -ETIMEDOUT;
}
@@ -293,44 +313,46 @@ static unsigned long clk_main_recalc_rate(struct regmap *regmap,
return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV;
}
-static int clk_rm9200_main_enable(struct clk *clk)
+static int clk_rm9200_main_prepare(struct clk_hw *hw)
{
- struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);
return clk_main_probe_frequency(clkmain->regmap);
}
-static int clk_rm9200_main_is_enabled(struct clk *clk)
+static int clk_rm9200_main_is_prepared(struct clk_hw *hw)
{
- struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);
unsigned int status;
regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);
- return status & AT91_PMC_MAINRDY ? 1 : 0;
+ return !!(status & AT91_PMC_MAINRDY);
}
-static unsigned long clk_rm9200_main_recalc_rate(struct clk *clk,
+static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);
return clk_main_recalc_rate(clkmain->regmap, parent_rate);
}
static const struct clk_ops rm9200_main_ops = {
- .enable = clk_rm9200_main_enable,
- .is_enabled = clk_rm9200_main_is_enabled,
+ .enable = clk_rm9200_main_prepare,
+ .is_enabled = clk_rm9200_main_is_prepared,
.recalc_rate = clk_rm9200_main_recalc_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
const char *parent_name)
{
- int ret;
struct clk_rm9200_main *clkmain;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name)
return ERR_PTR(-EINVAL);
@@ -338,22 +360,27 @@ at91_clk_register_rm9200_main(struct regmap *regmap,
if (!parent_name)
return ERR_PTR(-EINVAL);
- clkmain = xzalloc(sizeof(*clkmain));
+ clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
+ if (!clkmain)
+ return ERR_PTR(-ENOMEM);
- clkmain->parent = parent_name;
- clkmain->clk.name = name;
- clkmain->clk.ops = &rm9200_main_ops;
- clkmain->clk.parent_names = &clkmain->parent;
- clkmain->clk.num_parents = 1;
+ init.name = name;
+ init.ops = &rm9200_main_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = 0;
+
+ clkmain->hw.init = &init;
clkmain->regmap = regmap;
- ret = clk_register(&clkmain->clk);
+ hw = &clkmain->hw;
+ ret = clk_hw_register(NULL, &clkmain->hw);
if (ret) {
kfree(clkmain);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &clkmain->clk;
+ return hw;
}
static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
@@ -362,38 +389,38 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status);
- return status & AT91_PMC_MOSCSELS ? 1 : 0;
+ return !!(status & AT91_PMC_MOSCSELS);
}
-static int clk_sam9x5_main_enable(struct clk *clk)
+static int clk_sam9x5_main_prepare(struct clk_hw *hw)
{
- struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
struct regmap *regmap = clkmain->regmap;
while (!clk_sam9x5_main_ready(regmap))
- barrier();
+ cpu_relax();
return clk_main_probe_frequency(regmap);
}
-static int clk_sam9x5_main_is_enabled(struct clk *clk)
+static int clk_sam9x5_main_is_prepared(struct clk_hw *hw)
{
- struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
return clk_sam9x5_main_ready(clkmain->regmap);
}
-static unsigned long clk_sam9x5_main_recalc_rate(struct clk *clk,
+static unsigned long clk_sam9x5_main_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
return clk_main_recalc_rate(clkmain->regmap, parent_rate);
}
-static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index)
+static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index)
{
- struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
struct regmap *regmap = clkmain->regmap;
unsigned int tmp;
@@ -401,22 +428,27 @@ static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index)
return -EINVAL;
regmap_read(regmap, AT91_CKGR_MOR, &tmp);
- tmp &= ~MOR_KEY_MASK;
if (index && !(tmp & AT91_PMC_MOSCSEL))
- regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
+ tmp = AT91_PMC_MOSCSEL;
else if (!index && (tmp & AT91_PMC_MOSCSEL))
- regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
+ tmp = 0;
+ else
+ return 0;
+
+ regmap_update_bits(regmap, AT91_CKGR_MOR,
+ AT91_PMC_MOSCSEL | MOR_KEY_MASK,
+ tmp | AT91_PMC_KEY);
while (!clk_sam9x5_main_ready(regmap))
- barrier();
+ cpu_relax();
return 0;
}
-static int clk_sam9x5_main_get_parent(struct clk *clk)
+static int clk_sam9x5_main_get_parent(struct clk_hw *hw)
{
- struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
unsigned int status;
regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
@@ -425,23 +457,24 @@ static int clk_sam9x5_main_get_parent(struct clk *clk)
}
static const struct clk_ops sam9x5_main_ops = {
- .enable = clk_sam9x5_main_enable,
- .is_enabled = clk_sam9x5_main_is_enabled,
+ .enable = clk_sam9x5_main_prepare,
+ .is_enabled = clk_sam9x5_main_is_prepared,
.recalc_rate = clk_sam9x5_main_recalc_rate,
.set_parent = clk_sam9x5_main_set_parent,
.get_parent = clk_sam9x5_main_get_parent,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_sam9x5_main(struct regmap *regmap,
const char *name,
const char **parent_names,
int num_parents)
{
- int ret;
- unsigned int status;
- size_t parents_array_size;
struct clk_sam9x5_main *clkmain;
+ struct clk_init_data init;
+ unsigned int status;
+ struct clk_hw *hw;
+ int ret;
if (!name)
return ERR_PTR(-EINVAL);
@@ -449,25 +482,27 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
if (!parent_names || !num_parents)
return ERR_PTR(-EINVAL);
- clkmain = xzalloc(sizeof(*clkmain));
-
- clkmain->clk.name = name;
- clkmain->clk.ops = &sam9x5_main_ops;
- parents_array_size = num_parents * sizeof (clkmain->clk.parent_names[0]);
- clkmain->clk.parent_names = xmemdup(parent_names, parents_array_size);
- clkmain->clk.num_parents = num_parents;
+ clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
+ if (!clkmain)
+ return ERR_PTR(-ENOMEM);
- /* init.flags = CLK_SET_PARENT_GATE; */
+ init.name = name;
+ init.ops = &sam9x5_main_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_PARENT_GATE;
+ clkmain->hw.init = &init;
clkmain->regmap = regmap;
regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
clkmain->parent = clk_main_parent_select(status);
- ret = clk_register(&clkmain->clk);
+ hw = &clkmain->hw;
+ ret = clk_hw_register(NULL, &clkmain->hw);
if (ret) {
kfree(clkmain);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &clkmain->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index 4e3b512aaa..db5e235b6b 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -2,145 +2,462 @@
/*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
+#include <linux/printk.h>
#include "pmc.h"
-#define MASTER_SOURCE_MAX 4
-
#define MASTER_PRES_MASK 0x7
#define MASTER_PRES_MAX MASTER_PRES_MASK
#define MASTER_DIV_SHIFT 8
-#define MASTER_DIV_MASK 0x3
+#define MASTER_DIV_MASK 0x7
+
+#define PMC_MCR_CSS_SHIFT (16)
+
+#define MASTER_MAX_ID 4
-#define to_clk_master(clk) container_of(clk, struct clk_master, clk)
+#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
struct clk_master {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
+ spinlock_t *lock;
const struct clk_master_layout *layout;
const struct clk_master_characteristics *characteristics;
- const char *parents[MASTER_SOURCE_MAX];
+ struct at91_clk_pms pms;
+ u32 *mux_table;
u32 mckr;
+ int chg_pid;
+ u8 id;
+ u8 parent;
+ u8 div;
};
-static inline bool clk_master_ready(struct regmap *regmap)
+static inline bool clk_master_ready(struct clk_master *master)
{
+ unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
unsigned int status;
- regmap_read(regmap, AT91_PMC_SR, &status);
+ regmap_read(master->regmap, AT91_PMC_SR, &status);
- return status & AT91_PMC_MCKRDY ? 1 : 0;
+ return !!(status & bit);
}
-static int clk_master_enable(struct clk *clk)
+static int clk_master_prepare(struct clk_hw *hw)
{
- struct clk_master *master = to_clk_master(clk);
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ while (!clk_master_ready(master))
+ cpu_relax();
- while (!clk_master_ready(master->regmap))
- barrier();
+ spin_unlock_irqrestore(master->lock, flags);
return 0;
}
-static int clk_master_is_enabled(struct clk *clk)
+static int clk_master_is_prepared(struct clk_hw *hw)
{
- struct clk_master *master = to_clk_master(clk);
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ bool status;
- return clk_master_ready(master->regmap);
+ 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 *clk,
- 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;
- struct clk_master *master = to_clk_master(clk);
+ 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 int clk_master_get_parent(struct clk *clk)
+static const struct clk_ops master_div_ops = {
+ .enable = clk_master_prepare,
+ .is_enabled = clk_master_is_prepared,
+ .recalc_rate = clk_master_div_recalc_rate,
+};
+
+static unsigned long clk_master_div_recalc_rate_chg(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_master *master = to_clk_master(hw);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, master->div);
+}
+
+static const struct clk_ops master_div_ops_chg = {
+ .enable = clk_master_prepare,
+ .is_enabled = clk_master_is_prepared,
+ .recalc_rate = clk_master_div_recalc_rate_chg,
+};
+
+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);
+
+ val &= master->layout->mask;
+ pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
+ if (pres == MASTER_PRES_MAX && characteristics->have_div3_pres)
+ pres = 3;
+ else
+ pres = (1 << pres);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, pres);
+}
+
+static int clk_master_pres_get_parent(struct clk_hw *hw)
{
- struct clk_master *master = to_clk_master(clk);
+ 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);
+
+ mckr &= master->layout->mask;
return mckr & AT91_PMC_CSS;
}
-static const struct clk_ops master_ops = {
- .enable = clk_master_enable,
- .is_enabled = clk_master_is_enabled,
- .recalc_rate = clk_master_recalc_rate,
- .get_parent = clk_master_get_parent,
+static const struct clk_ops master_pres_ops = {
+ .enable = clk_master_prepare,
+ .is_enabled = clk_master_is_prepared,
+ .recalc_rate = clk_master_pres_recalc_rate,
+ .get_parent = clk_master_pres_get_parent,
};
-struct clk * __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)
+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_ops *ops, spinlock_t *lock, u32 flags)
{
- int ret;
- const size_t parent_names_size = num_parents * sizeof(parent_names[0]);
struct clk_master *master;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ unsigned int mckr;
+ unsigned long irqflags;
+ int ret;
- if (!name || !num_parents || !parent_names)
+ if (!name || !num_parents || !parent_names || !lock)
return ERR_PTR(-EINVAL);
- master = xzalloc(sizeof(*master));
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return ERR_PTR(-ENOMEM);
- master->clk.name = name;
- master->clk.ops = &master_ops;
- memcpy(master->parents, parent_names, parent_names_size);
- master->clk.parent_names = master->parents;
- master->clk.num_parents = num_parents;
+ init.name = name;
+ init.ops = ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = flags;
+ master->hw.init = &init;
master->layout = layout;
master->characteristics = characteristics;
master->regmap = regmap;
+ master->lock = lock;
+
+ if (ops == &master_div_ops_chg) {
+ spin_lock_irqsave(master->lock, irqflags);
+ regmap_read(master->regmap, master->layout->offset, &mckr);
+ spin_unlock_irqrestore(master->lock, irqflags);
- ret = clk_register(&master->clk);
+ mckr &= layout->mask;
+ mckr = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+ master->div = characteristics->divisors[mckr];
+ }
+
+ hw = &master->hw;
+ ret = clk_hw_register(NULL, &master->hw);
if (ret) {
kfree(master);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &master->clk;
+ return hw;
+}
+
+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)
+{
+ return at91_clk_register_master_internal(regmap, name, num_parents,
+ parent_names, layout,
+ characteristics,
+ &master_pres_ops,
+ lock, CLK_SET_RATE_GATE);
+}
+
+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)
+{
+ const struct clk_ops *ops;
+
+ if (flags & CLK_SET_RATE_GATE)
+ ops = &master_div_ops;
+ else
+ ops = &master_div_ops_chg;
+
+ return at91_clk_register_master_internal(regmap, name, 1,
+ &parent_name, layout,
+ characteristics, ops,
+ lock, flags);
+}
+
+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_get_parent(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ u8 index;
+
+ spin_lock_irqsave(master->lock, flags);
+ index = clk_mux_val_to_index(&master->hw, master->mux_table, 0,
+ master->parent);
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return index;
+}
+
+static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+
+ if (index >= clk_hw_get_num_parents(hw))
+ return -EINVAL;
+
+ spin_lock_irqsave(master->lock, flags);
+ master->parent = clk_mux_index_to_val(master->mux_table, 0, index);
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return 0;
+}
+
+static void clk_sama7g5_master_set(struct clk_master *master,
+ unsigned int status)
+{
+ unsigned long flags;
+ unsigned int val, cparent;
+ unsigned int enable = status ? AT91_PMC_MCR_V2_EN : 0;
+ unsigned int parent = master->parent << PMC_MCR_CSS_SHIFT;
+ unsigned int div = master->div << MASTER_DIV_SHIFT;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, AT91_PMC_MCR_V2,
+ AT91_PMC_MCR_V2_ID(master->id));
+ regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
+ regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
+ enable | AT91_PMC_MCR_V2_CSS | AT91_PMC_MCR_V2_DIV |
+ AT91_PMC_MCR_V2_CMD | AT91_PMC_MCR_V2_ID_MSK,
+ enable | parent | div | AT91_PMC_MCR_V2_CMD |
+ AT91_PMC_MCR_V2_ID(master->id));
+
+ cparent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
+
+ /* Wait here only if parent is being changed. */
+ while ((cparent != master->parent) && !clk_master_ready(master))
+ cpu_relax();
+
+ spin_unlock_irqrestore(master->lock, flags);
+}
+
+static int clk_sama7g5_master_enable(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+
+ clk_sama7g5_master_set(master, 1);
+
+ return 0;
+}
+
+static void clk_sama7g5_master_disable(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+ regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
+ AT91_PMC_MCR_V2_EN | AT91_PMC_MCR_V2_CMD |
+ AT91_PMC_MCR_V2_ID_MSK,
+ AT91_PMC_MCR_V2_CMD |
+ AT91_PMC_MCR_V2_ID(master->id));
+
+ spin_unlock_irqrestore(master->lock, flags);
+}
+
+static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+ regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
+
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return !!(val & AT91_PMC_MCR_V2_EN);
}
+static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long div, flags;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1)))
+ return -EINVAL;
+
+ if (div == 3)
+ div = MASTER_PRES_MAX;
+ else if (div)
+ div = ffs(div) - 1;
+
+ spin_lock_irqsave(master->lock, flags);
+ master->div = div;
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops sama7g5_master_ops = {
+ .enable = clk_sama7g5_master_enable,
+ .disable = clk_sama7g5_master_disable,
+ .is_enabled = clk_sama7g5_master_is_enabled,
+ .recalc_rate = clk_sama7g5_master_recalc_rate,
+ .set_rate = clk_sama7g5_master_set_rate,
+ .get_parent = clk_sama7g5_master_get_parent,
+ .set_parent = clk_sama7g5_master_set_parent,
+};
+
+struct clk_hw * __init
+at91_clk_sama7g5_register_master(struct regmap *regmap,
+ const char *name, int num_parents,
+ const char **parent_names,
+ u32 *mux_table,
+ spinlock_t *lock, u8 id,
+ bool critical, int chg_pid)
+{
+ struct clk_master *master;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ unsigned long flags;
+ unsigned int val;
+ int ret;
+
+ if (!name || !num_parents || !parent_names || !mux_table ||
+ !lock || id > MASTER_MAX_ID)
+ return ERR_PTR(-EINVAL);
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &sama7g5_master_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+ if (chg_pid >= 0)
+ init.flags |= CLK_SET_RATE_PARENT;
+ if (critical)
+ init.flags |= CLK_IS_CRITICAL;
+
+ master->hw.init = &init;
+ master->regmap = regmap;
+ master->id = id;
+ master->chg_pid = chg_pid;
+ master->lock = lock;
+ master->mux_table = mux_table;
+
+ spin_lock_irqsave(master->lock, flags);
+ regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
+ regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
+ master->parent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
+ master->div = (val & AT91_PMC_MCR_V2_DIV) >> MASTER_DIV_SHIFT;
+ spin_unlock_irqrestore(master->lock, flags);
+
+ hw = &master->hw;
+ ret = clk_hw_register(NULL, &master->hw);
+ if (ret) {
+ kfree(master);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
const struct clk_master_layout at91rm9200_master_layout = {
.mask = 0x31F,
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index 2b9008eb2c..bd4b50b142 100644
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -3,51 +3,51 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
+DEFINE_SPINLOCK(pmc_pcr_lock);
+
#define PERIPHERAL_ID_MIN 2
#define PERIPHERAL_ID_MAX 31
#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
-#define PERIPHERAL_RSHIFT_MASK 0x3
-#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
-
#define PERIPHERAL_MAX_SHIFT 3
struct clk_peripheral {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
u32 id;
- const char *parent;
};
-#define to_clk_peripheral(clk) container_of(clk, struct clk_peripheral, clk)
+#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
struct clk_sam9x5_peripheral {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
struct clk_range range;
+ spinlock_t *lock;
u32 id;
u32 div;
const struct clk_pcr_layout *layout;
+ struct at91_clk_pms pms;
bool auto_div;
- const char *parent;
+ int chg_pid;
};
-#define to_clk_sam9x5_peripheral(clk) \
- container_of(clk, struct clk_sam9x5_peripheral, clk)
+#define to_clk_sam9x5_peripheral(hw) \
+ container_of(hw, struct clk_sam9x5_peripheral, hw)
-static int clk_peripheral_enable(struct clk *clk)
+static int clk_peripheral_enable(struct clk_hw *hw)
{
- struct clk_peripheral *periph = to_clk_peripheral(clk);
+ struct clk_peripheral *periph = to_clk_peripheral(hw);
int offset = AT91_PMC_PCER;
u32 id = periph->id;
@@ -60,9 +60,9 @@ static int clk_peripheral_enable(struct clk *clk)
return 0;
}
-static void clk_peripheral_disable(struct clk *clk)
+static void clk_peripheral_disable(struct clk_hw *hw)
{
- struct clk_peripheral *periph = to_clk_peripheral(clk);
+ struct clk_peripheral *periph = to_clk_peripheral(hw);
int offset = AT91_PMC_PCDR;
u32 id = periph->id;
@@ -73,9 +73,9 @@ static void clk_peripheral_disable(struct clk *clk)
regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
}
-static int clk_peripheral_is_enabled(struct clk *clk)
+static int clk_peripheral_is_enabled(struct clk_hw *hw)
{
- struct clk_peripheral *periph = to_clk_peripheral(clk);
+ struct clk_peripheral *periph = to_clk_peripheral(hw);
int offset = AT91_PMC_PCSR;
unsigned int status;
u32 id = periph->id;
@@ -95,42 +95,45 @@ static const struct clk_ops peripheral_ops = {
.is_enabled = clk_peripheral_is_enabled,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id)
{
- int ret;
struct clk_peripheral *periph;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
return ERR_PTR(-EINVAL);
- periph = xzalloc(sizeof(*periph));
-
- periph->clk.name = name;
- periph->clk.ops = &peripheral_ops;
+ periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+ if (!periph)
+ return ERR_PTR(-ENOMEM);
- if (parent_name) {
- periph->parent = parent_name;
- periph->clk.parent_names = &periph->parent;
- periph->clk.num_parents = 1;
- }
+ init.name = name;
+ init.ops = &peripheral_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = 0;
periph->id = id;
+ periph->hw.init = &init;
periph->regmap = regmap;
- ret = clk_register(&periph->clk);
+ hw = &periph->hw;
+ ret = clk_hw_register(NULL, &periph->hw);
if (ret) {
kfree(periph);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &periph->clk;
+ return hw;
}
static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
{
- struct clk *parent;
+ struct clk_hw *parent;
unsigned long parent_rate;
int shift = 0;
@@ -138,8 +141,8 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
return;
if (periph->range.max) {
- parent = clk_get_parent(&periph->clk);
- parent_rate = clk_get_rate(parent);
+ parent = clk_hw_get_parent_by_index(&periph->hw, 0);
+ parent_rate = clk_hw_get_rate(parent);
if (!parent_rate)
return;
@@ -153,67 +156,86 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
periph->div = shift;
}
-static int clk_sam9x5_peripheral_enable(struct clk *clk)
+static int clk_sam9x5_peripheral_set(struct clk_sam9x5_peripheral *periph,
+ unsigned int status)
{
- struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ unsigned long flags;
+ unsigned int enable = status ? AT91_PMC_PCR_EN : 0;
if (periph->id < PERIPHERAL_ID_MIN)
return 0;
+ spin_lock_irqsave(periph->lock, flags);
regmap_write(periph->regmap, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
- regmap_write_bits(periph->regmap, periph->layout->offset,
- periph->layout->div_mask | periph->layout->cmd |
- AT91_PMC_PCR_EN,
- field_prep(periph->layout->div_mask, periph->div) |
- periph->layout->cmd |
- AT91_PMC_PCR_EN);
+ regmap_update_bits(periph->regmap, periph->layout->offset,
+ periph->layout->div_mask | periph->layout->cmd |
+ enable,
+ field_prep(periph->layout->div_mask, periph->div) |
+ periph->layout->cmd | enable);
+ spin_unlock_irqrestore(periph->lock, flags);
return 0;
}
-static void clk_sam9x5_peripheral_disable(struct clk *clk)
+static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
{
- struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+
+ return clk_sam9x5_peripheral_set(periph, 1);
+}
+
+static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
+{
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+ unsigned long flags;
if (periph->id < PERIPHERAL_ID_MIN)
return;
+ spin_lock_irqsave(periph->lock, flags);
regmap_write(periph->regmap, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
- regmap_write_bits(periph->regmap, periph->layout->offset,
- AT91_PMC_PCR_EN | periph->layout->cmd,
- periph->layout->cmd);
+ regmap_update_bits(periph->regmap, periph->layout->offset,
+ AT91_PMC_PCR_EN | periph->layout->cmd,
+ periph->layout->cmd);
+ spin_unlock_irqrestore(periph->lock, flags);
}
-static int clk_sam9x5_peripheral_is_enabled(struct clk *clk)
+static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
{
- struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+ unsigned long flags;
unsigned int status;
if (periph->id < PERIPHERAL_ID_MIN)
return 1;
+ spin_lock_irqsave(periph->lock, flags);
regmap_write(periph->regmap, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
regmap_read(periph->regmap, periph->layout->offset, &status);
+ spin_unlock_irqrestore(periph->lock, flags);
- return status & AT91_PMC_PCR_EN ? 1 : 0;
+ return !!(status & AT91_PMC_PCR_EN);
}
static unsigned long
-clk_sam9x5_peripheral_recalc_rate(struct clk *clk,
+clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+ unsigned long flags;
unsigned int status;
if (periph->id < PERIPHERAL_ID_MIN)
return parent_rate;
+ spin_lock_irqsave(periph->lock, flags);
regmap_write(periph->regmap, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
regmap_read(periph->regmap, periph->layout->offset, &status);
+ spin_unlock_irqrestore(periph->lock, flags);
if (status & AT91_PMC_PCR_EN) {
periph->div = field_get(periph->layout->div_mask, status);
@@ -225,7 +247,7 @@ clk_sam9x5_peripheral_recalc_rate(struct clk *clk,
return parent_rate >> periph->div;
}
-static long clk_sam9x5_peripheral_round_rate(struct clk *clk,
+static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate)
{
@@ -234,7 +256,7 @@ static long clk_sam9x5_peripheral_round_rate(struct clk *clk,
unsigned long best_diff;
unsigned long cur_rate = *parent_rate;
unsigned long cur_diff;
- struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
return *parent_rate;
@@ -271,12 +293,12 @@ static long clk_sam9x5_peripheral_round_rate(struct clk *clk,
return best_rate;
}
-static int clk_sam9x5_peripheral_set_rate(struct clk *clk,
+static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
int shift;
- struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
if (parent_rate == rate)
return 0;
@@ -307,45 +329,64 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.set_rate = clk_sam9x5_peripheral_set_rate,
};
-struct clk * __init
-at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
+static const struct clk_ops sam9x5_peripheral_chg_ops = {
+ .enable = clk_sam9x5_peripheral_enable,
+ .disable = clk_sam9x5_peripheral_disable,
+ .is_enabled = clk_sam9x5_peripheral_is_enabled,
+ .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
+ .set_rate = clk_sam9x5_peripheral_set_rate,
+};
+
+struct clk_hw * __init
+at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
- u32 id, const struct clk_range *range)
+ u32 id, const struct clk_range *range,
+ int chg_pid, unsigned long flags)
{
- int ret;
struct clk_sam9x5_peripheral *periph;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !parent_name)
return ERR_PTR(-EINVAL);
- periph = xzalloc(sizeof(*periph));
-
- periph->clk.name = name;
- periph->clk.ops = &sam9x5_peripheral_ops;
+ periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+ if (!periph)
+ return ERR_PTR(-ENOMEM);
- if (parent_name) {
- periph->parent = parent_name;
- periph->clk.parent_names = &periph->parent;
- periph->clk.num_parents = 1;
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = flags;
+ if (chg_pid < 0) {
+ init.ops = &sam9x5_peripheral_ops;
+ } else {
+ init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+ init.ops = &sam9x5_peripheral_chg_ops;
}
periph->id = id;
+ periph->hw.init = &init;
periph->div = 0;
periph->regmap = regmap;
+ periph->lock = lock;
if (layout->div_mask)
periph->auto_div = true;
periph->layout = layout;
periph->range = *range;
+ periph->chg_pid = chg_pid;
- ret = clk_register(&periph->clk);
+ hw = &periph->hw;
+ ret = clk_hw_register(NULL, &periph->hw);
if (ret) {
kfree(periph);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
+ } else {
+ clk_sam9x5_peripheral_autodiv(periph);
}
- clk_sam9x5_peripheral_autodiv(periph);
- pmc_register_id(id);
-
- return &periph->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index 5cb156e784..027e1fc773 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -3,14 +3,12 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <of.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
@@ -31,10 +29,10 @@
#define PLL_OUT_SHIFT 14
#define PLL_MAX_ID 1
-#define to_clk_pll(clk) container_of(clk, struct clk_pll, clk)
+#define to_clk_pll(hw) container_of(hw, struct clk_pll, hw)
struct clk_pll {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
u8 id;
u8 div;
@@ -42,7 +40,7 @@ struct clk_pll {
u16 mul;
const struct clk_pll_layout *layout;
const struct clk_pll_characteristics *characteristics;
- const char *parent;
+ struct at91_clk_pms pms;
};
static inline bool clk_pll_ready(struct regmap *regmap, int id)
@@ -54,9 +52,9 @@ static inline bool clk_pll_ready(struct regmap *regmap, int id)
return status & PLL_STATUS_MASK(id) ? 1 : 0;
}
-static int clk_pll_enable(struct clk *clk)
+static int clk_pll_prepare(struct clk_hw *hw)
{
- struct clk_pll *pll = to_clk_pll(clk);
+ struct clk_pll *pll = to_clk_pll(hw);
struct regmap *regmap = pll->regmap;
const struct clk_pll_layout *layout = pll->layout;
const struct clk_pll_characteristics *characteristics =
@@ -83,39 +81,39 @@ static int clk_pll_enable(struct clk *clk)
out = characteristics->out[pll->range];
if (characteristics->icpll)
- regmap_write_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id),
+ regmap_update_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id),
characteristics->icpll[pll->range] << PLL_ICPR_SHIFT(id));
- regmap_write_bits(regmap, offset, layout->pllr_mask,
- pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
- (out << PLL_OUT_SHIFT) |
- ((pll->mul & layout->mul_mask) << layout->mul_shift));
+ regmap_update_bits(regmap, offset, layout->pllr_mask,
+ pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
+ (out << PLL_OUT_SHIFT) |
+ ((pll->mul & layout->mul_mask) << layout->mul_shift));
while (!clk_pll_ready(regmap, pll->id))
- barrier();
+ cpu_relax();
return 0;
}
-static int clk_pll_is_enabled(struct clk *clk)
+static int clk_pll_is_prepared(struct clk_hw *hw)
{
- struct clk_pll *pll = to_clk_pll(clk);
+ struct clk_pll *pll = to_clk_pll(hw);
return clk_pll_ready(pll->regmap, pll->id);
}
-static void clk_pll_disable(struct clk *clk)
+static void clk_pll_unprepare(struct clk_hw *hw)
{
- struct clk_pll *pll = to_clk_pll(clk);
+ struct clk_pll *pll = to_clk_pll(hw);
unsigned int mask = pll->layout->pllr_mask;
- regmap_write_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask);
+ regmap_update_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask);
}
-static unsigned long clk_pll_recalc_rate(struct clk *clk,
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_pll *pll = to_clk_pll(clk);
+ struct clk_pll *pll = to_clk_pll(hw);
if (!pll->div || !pll->mul)
return 0;
@@ -233,19 +231,19 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
return bestrate;
}
-static long clk_pll_round_rate(struct clk *clk, unsigned long rate,
- unsigned long *parent_rate)
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
{
- struct clk_pll *pll = to_clk_pll(clk);
+ struct clk_pll *pll = to_clk_pll(hw);
return clk_pll_get_best_div_mul(pll, rate, *parent_rate,
NULL, NULL, NULL);
}
-static int clk_pll_set_rate(struct clk *clk, unsigned long rate,
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct clk_pll *pll = to_clk_pll(clk);
+ struct clk_pll *pll = to_clk_pll(hw);
long ret;
u32 div;
u32 mul;
@@ -264,21 +262,23 @@ static int clk_pll_set_rate(struct clk *clk, unsigned long rate,
}
static const struct clk_ops pll_ops = {
- .enable = clk_pll_enable,
- .disable = clk_pll_disable,
- .is_enabled = clk_pll_is_enabled,
+ .enable = clk_pll_prepare,
+ .disable = clk_pll_unprepare,
+ .is_enabled = clk_pll_is_prepared,
.recalc_rate = clk_pll_recalc_rate,
.round_rate = clk_pll_round_rate,
.set_rate = clk_pll_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_pll(struct regmap *regmap, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_layout *layout,
const struct clk_pll_characteristics *characteristics)
{
struct clk_pll *pll;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int offset = PLL_REG(id);
unsigned int pllr;
int ret;
@@ -286,17 +286,18 @@ at91_clk_register_pll(struct regmap *regmap, const char *name,
if (id > PLL_MAX_ID)
return ERR_PTR(-EINVAL);
- pll = xzalloc(sizeof(*pll));
-
- pll->parent = parent_name;
- pll->clk.name = name;
- pll->clk.ops = &pll_ops;
- pll->clk.parent_names = &pll->parent;
- pll->clk.num_parents = 1;
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
- /* init.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = &pll_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE;
pll->id = id;
+ pll->hw.init = &init;
pll->layout = layout;
pll->characteristics = characteristics;
pll->regmap = regmap;
@@ -304,13 +305,14 @@ at91_clk_register_pll(struct regmap *regmap, const char *name,
pll->div = PLL_DIV(pllr);
pll->mul = PLL_MUL(pllr, layout);
- ret = clk_register(&pll->clk);
+ hw = &pll->hw;
+ ret = clk_hw_register(NULL, &pll->hw);
if (ret) {
kfree(pll);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &pll->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c
index 1cbb61bb2c..7fe4411149 100644
--- a/drivers/clk/at91/clk-plldiv.c
+++ b/drivers/clk/at91/clk-plldiv.c
@@ -3,29 +3,26 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <of.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define to_clk_plldiv(hw) container_of(clk, struct clk_plldiv, clk)
+#define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw)
struct clk_plldiv {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
- const char *parent;
};
-static unsigned long clk_plldiv_recalc_rate(struct clk *clk,
+static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_plldiv *plldiv = to_clk_plldiv(clk);
+ struct clk_plldiv *plldiv = to_clk_plldiv(hw);
unsigned int mckr;
regmap_read(plldiv->regmap, AT91_PMC_MCKR, &mckr);
@@ -36,8 +33,8 @@ static unsigned long clk_plldiv_recalc_rate(struct clk *clk,
return parent_rate;
}
-static long clk_plldiv_round_rate(struct clk *clk, unsigned long rate,
- unsigned long *parent_rate)
+static long clk_plldiv_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
{
unsigned long div;
@@ -53,16 +50,16 @@ static long clk_plldiv_round_rate(struct clk *clk, unsigned long rate,
return *parent_rate;
}
-static int clk_plldiv_set_rate(struct clk *clk, unsigned long rate,
+static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct clk_plldiv *plldiv = to_clk_plldiv(clk);
+ struct clk_plldiv *plldiv = to_clk_plldiv(hw);
if ((parent_rate != rate) && (parent_rate / 2 != rate))
return -EINVAL;
- regmap_write_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2,
- parent_rate != rate ? AT91_PMC_PLLADIV2 : 0);
+ regmap_update_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2,
+ parent_rate != rate ? AT91_PMC_PLLADIV2 : 0);
return 0;
}
@@ -73,33 +70,34 @@ static const struct clk_ops plldiv_ops = {
.set_rate = clk_plldiv_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name)
{
- int ret;
struct clk_plldiv *plldiv;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
- plldiv = xzalloc(sizeof(*plldiv));
-
- plldiv->clk.name = name;
- plldiv->clk.ops = &plldiv_ops;
-
- if (parent_name) {
- plldiv->parent = parent_name;
- plldiv->clk.parent_names = &plldiv->parent;
- plldiv->clk.num_parents = 1;
- }
+ plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL);
+ if (!plldiv)
+ return ERR_PTR(-ENOMEM);
- /* init.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = &plldiv_ops;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.flags = CLK_SET_RATE_GATE;
+ plldiv->hw.init = &init;
plldiv->regmap = regmap;
- ret = clk_register(&plldiv->clk);
+ hw = &plldiv->hw;
+ ret = clk_hw_register(NULL, &plldiv->hw);
if (ret) {
kfree(plldiv);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &plldiv->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index 26c36a882d..3bf13568f5 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -3,18 +3,15 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define PROG_SOURCE_MAX 5
#define PROG_ID_MAX 7
#define PROG_STATUS_MASK(id) (1 << ((id) + 8))
@@ -22,19 +19,20 @@
#define PROG_MAX_RM9200_CSS 3
struct clk_programmable {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
+ u32 *mux_table;
u8 id;
const struct clk_programmable_layout *layout;
- const char *parent_names[PROG_SOURCE_MAX];
+ struct at91_clk_pms pms;
};
-#define to_clk_programmable(clk) container_of(clk, struct clk_programmable, clk)
+#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
-static unsigned long clk_programmable_recalc_rate(struct clk *clk,
+static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_programmable *prog = to_clk_programmable(clk);
+ struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
unsigned int pckr;
unsigned long rate;
@@ -49,9 +47,9 @@ static unsigned long clk_programmable_recalc_rate(struct clk *clk,
return rate;
}
-static int clk_programmable_set_parent(struct clk *clk, u8 index)
+static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
{
- struct clk_programmable *prog = to_clk_programmable(clk);
+ struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
unsigned int mask = layout->css_mask;
unsigned int pckr = index;
@@ -59,6 +57,9 @@ static int clk_programmable_set_parent(struct clk *clk, u8 index)
if (layout->have_slck_mck)
mask |= AT91_PMC_CSSMCK_MCK;
+ if (prog->mux_table)
+ pckr = clk_mux_index_to_val(prog->mux_table, 0, index);
+
if (index > layout->css_mask) {
if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
return -EINVAL;
@@ -66,14 +67,14 @@ static int clk_programmable_set_parent(struct clk *clk, u8 index)
pckr |= AT91_PMC_CSSMCK_MCK;
}
- regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr);
+ regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr);
return 0;
}
-static int clk_programmable_get_parent(struct clk *clk)
+static int clk_programmable_get_parent(struct clk_hw *hw)
{
- struct clk_programmable *prog = to_clk_programmable(clk);
+ struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
unsigned int pckr;
u8 ret;
@@ -85,13 +86,16 @@ static int clk_programmable_get_parent(struct clk *clk)
if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
ret = PROG_MAX_RM9200_CSS + 1;
+ if (prog->mux_table)
+ ret = clk_mux_val_to_index(&prog->hw, prog->mux_table, 0, ret);
+
return ret;
}
-static int clk_programmable_set_rate(struct clk *clk, unsigned long rate,
+static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct clk_programmable *prog = to_clk_programmable(clk);
+ struct clk_programmable *prog = to_clk_programmable(hw);
const struct clk_programmable_layout *layout = prog->layout;
unsigned long div = parent_rate / rate;
int shift = 0;
@@ -114,9 +118,9 @@ static int clk_programmable_set_rate(struct clk *clk, unsigned long rate,
return -EINVAL;
}
- regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
- layout->pres_mask << layout->pres_shift,
- shift << layout->pres_shift);
+ regmap_update_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
+ layout->pres_mask << layout->pres_shift,
+ shift << layout->pres_shift);
return 0;
}
@@ -128,13 +132,16 @@ static const struct clk_ops programmable_ops = {
.set_rate = clk_programmable_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap,
const char *name, const char **parent_names,
u8 num_parents, u8 id,
- const struct clk_programmable_layout *layout)
+ const struct clk_programmable_layout *layout,
+ u32 *mux_table)
{
struct clk_programmable *prog;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (id > PROG_ID_MAX)
@@ -144,27 +151,26 @@ at91_clk_register_programmable(struct regmap *regmap,
if (!prog)
return ERR_PTR(-ENOMEM);
- prog->clk.name = name;
- prog->clk.ops = &programmable_ops;
- memcpy(prog->parent_names, parent_names,
- num_parents * sizeof(prog->parent_names[0]));
- prog->clk.parent_names = &prog->parent_names[0];
- prog->clk.num_parents = num_parents;
- /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
+ init.name = name;
+ init.ops = &programmable_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
prog->id = id;
prog->layout = layout;
+ prog->hw.init = &init;
prog->regmap = regmap;
+ prog->mux_table = mux_table;
- ret = clk_register(&prog->clk);
+ hw = &prog->hw;
+ ret = clk_hw_register(NULL, &prog->hw);
if (ret) {
kfree(prog);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- pmc_register_pck(id);
-
- return &prog->clk;
+ return hw;
}
const struct clk_programmable_layout at91rm9200_programmable_layout = {
diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index 9ca77f8722..c4f1606128 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -4,319 +4,654 @@
*
*/
-#include <common.h>
-#include <clock.h>
-#include <of.h>
-#include <linux/list.h>
+#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
-#include <linux/bitfield.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define PMC_PLL_CTRL0 0xc
-#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
-#define PMC_PLL_CTRL0_ENPLL BIT(28)
-#define PMC_PLL_CTRL0_ENPLLCK BIT(29)
-#define PMC_PLL_CTRL0_ENLOCK BIT(31)
-
-#define PMC_PLL_CTRL1 0x10
-#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
-#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24)
-
-#define PMC_PLL_ACR 0x18
-#define PMC_PLL_ACR_DEFAULT_UPLL 0x12020010UL
-#define PMC_PLL_ACR_DEFAULT_PLLA 0x00020010UL
-#define PMC_PLL_ACR_UTMIVR BIT(12)
-#define PMC_PLL_ACR_UTMIBG BIT(13)
-#define PMC_PLL_ACR_LOOP_FILTER_MSK GENMASK(31, 24)
-
-#define PMC_PLL_UPDT 0x1c
-#define PMC_PLL_UPDT_UPDATE BIT(8)
-
-#define PMC_PLL_ISR0 0xec
+#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
+#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
+#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
#define UPLL_DIV 2
#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
-#define PLL_MAX_ID 1
+#define FCORE_MIN (600000000)
+#define FCORE_MAX (1200000000)
-struct sam9x60_pll {
- struct clk clk;
+#define PLL_MAX_ID 7
+
+struct sam9x60_pll_core {
struct regmap *regmap;
+ spinlock_t *lock;
const struct clk_pll_characteristics *characteristics;
- u32 frac;
+ const struct clk_pll_layout *layout;
+ struct clk_hw hw;
u8 id;
- u8 div;
+};
+
+struct sam9x60_frac {
+ struct sam9x60_pll_core core;
+ struct at91_clk_pms pms;
+ u32 frac;
u16 mul;
- const char *parent_name;
};
-#define to_sam9x60_pll(clk) container_of(clk, struct sam9x60_pll, clk)
+struct sam9x60_div {
+ struct sam9x60_pll_core core;
+ struct at91_clk_pms pms;
+ u8 div;
+};
+
+#define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
+#define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
+#define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)
static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
{
unsigned int status;
- regmap_read(regmap, PMC_PLL_ISR0, &status);
+ regmap_read(regmap, AT91_PMC_PLL_ISR0, &status);
return !!(status & BIT(id));
}
-static int sam9x60_pll_enable(struct clk *clk)
+static bool sam9x60_frac_pll_ready(struct regmap *regmap, u8 id)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(clk);
- struct regmap *regmap = pll->regmap;
- u8 div;
- u16 mul;
- u32 val;
+ return sam9x60_pll_ready(regmap, id);
+}
- regmap_write(regmap, PMC_PLL_UPDT, pll->id);
+static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
- regmap_read(regmap, PMC_PLL_CTRL0, &val);
- div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
+ return parent_rate * (frac->mul + 1) +
+ DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22));
+}
- regmap_read(regmap, PMC_PLL_CTRL1, &val);
- mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
+static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
+{
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ struct regmap *regmap = core->regmap;
+ unsigned int val, cfrac, cmul;
+ unsigned long flags;
- if (sam9x60_pll_ready(regmap, pll->id) &&
- (div == pll->div && mul == pll->mul)) {
- return 0;
- }
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
+ cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
+ cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
+
+ if (sam9x60_frac_pll_ready(regmap, core->id) &&
+ (cmul == frac->mul && cfrac == frac->frac))
+ goto unlock;
/* Recommended value for PMC_PLL_ACR */
- if (pll->characteristics->upll)
- val = PMC_PLL_ACR_DEFAULT_UPLL;
+ if (core->characteristics->upll)
+ val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
else
- val = PMC_PLL_ACR_DEFAULT_PLLA;
- regmap_write(regmap, PMC_PLL_ACR, val);
+ val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
+ regmap_write(regmap, AT91_PMC_PLL_ACR, val);
- regmap_write(regmap, PMC_PLL_CTRL1,
- FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul));
+ regmap_write(regmap, AT91_PMC_PLL_CTRL1,
+ (frac->mul << core->layout->mul_shift) |
+ (frac->frac << core->layout->frac_shift));
- if (pll->characteristics->upll) {
+ if (core->characteristics->upll) {
/* Enable the UTMI internal bandgap */
- val |= PMC_PLL_ACR_UTMIBG;
- regmap_write(regmap, PMC_PLL_ACR, val);
+ val |= AT91_PMC_PLL_ACR_UTMIBG;
+ regmap_write(regmap, AT91_PMC_PLL_ACR, val);
udelay(10);
/* Enable the UTMI internal regulator */
- val |= PMC_PLL_ACR_UTMIVR;
- regmap_write(regmap, PMC_PLL_ACR, val);
+ val |= AT91_PMC_PLL_ACR_UTMIVR;
+ regmap_write(regmap, AT91_PMC_PLL_ACR, val);
udelay(10);
}
- regmap_update_bits(regmap, PMC_PLL_UPDT,
- PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
- regmap_write(regmap, PMC_PLL_CTRL0,
- PMC_PLL_CTRL0_ENLOCK | PMC_PLL_CTRL0_ENPLL |
- PMC_PLL_CTRL0_ENPLLCK | pll->div);
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
+ AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
- regmap_update_bits(regmap, PMC_PLL_UPDT,
- PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
- while (!sam9x60_pll_ready(regmap, pll->id))
+ while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
+unlock:
+ spin_unlock_irqrestore(core->lock, flags);
+
return 0;
}
-static int sam9x60_pll_is_enabled(struct clk *clk)
+static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
- return sam9x60_pll_ready(pll->regmap, pll->id);
+ return sam9x60_frac_pll_set(core);
}
-static void sam9x60_pll_disable(struct clk *clk)
+static void sam9x60_frac_pll_unprepare(struct clk_hw *hw)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
- regmap_write(pll->regmap, PMC_PLL_UPDT, pll->id);
+ spin_lock_irqsave(core->lock, flags);
- regmap_update_bits(pll->regmap, PMC_PLL_CTRL0,
- PMC_PLL_CTRL0_ENPLLCK, 0);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
- regmap_update_bits(pll->regmap, PMC_PLL_UPDT,
- PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0);
- regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, PMC_PLL_CTRL0_ENPLL, 0);
+ if (core->characteristics->upll)
+ regmap_update_bits(regmap, AT91_PMC_PLL_ACR,
+ AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR, 0);
- if (pll->characteristics->upll)
- regmap_update_bits(pll->regmap, PMC_PLL_ACR,
- PMC_PLL_ACR_UTMIBG | PMC_PLL_ACR_UTMIVR, 0);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
- regmap_update_bits(pll->regmap, PMC_PLL_UPDT,
- PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
+ spin_unlock_irqrestore(core->lock, flags);
}
-static unsigned long sam9x60_pll_recalc_rate(struct clk *clk,
- unsigned long parent_rate)
+static int sam9x60_frac_pll_is_prepared(struct clk_hw *hw)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
- return (parent_rate * (pll->mul + 1)) / (pll->div + 1);
+ return sam9x60_pll_ready(core->regmap, core->id);
}
-static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
- unsigned long rate,
- unsigned long parent_rate,
- bool update)
+static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
+ unsigned long rate,
+ unsigned long parent_rate,
+ bool update)
{
- const struct clk_pll_characteristics *characteristics =
- pll->characteristics;
- unsigned long bestremainder = ULONG_MAX;
- unsigned long maxdiv, mindiv, tmpdiv;
- long bestrate = -ERANGE;
- unsigned long bestdiv = 0;
- unsigned long bestmul = 0;
- unsigned long bestfrac = 0;
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ unsigned long tmprate, remainder;
+ unsigned long nmul = 0;
+ unsigned long nfrac = 0;
- if (rate < characteristics->output[0].min ||
- rate > characteristics->output[0].max)
+ if (rate < FCORE_MIN || rate > FCORE_MAX)
return -ERANGE;
- if (!pll->characteristics->upll) {
- mindiv = parent_rate / rate;
- if (mindiv < 2)
- mindiv = 2;
+ /*
+ * Calculate the multiplier associated with the current
+ * divider that provide the closest rate to the requested one.
+ */
+ nmul = mult_frac(rate, 1, parent_rate);
+ tmprate = mult_frac(parent_rate, nmul, 1);
+ remainder = rate - tmprate;
- maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate);
- if (maxdiv > PLL_DIV_MAX)
- maxdiv = PLL_DIV_MAX;
- } else {
- mindiv = maxdiv = UPLL_DIV;
+ if (remainder) {
+ nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
+ parent_rate);
+
+ tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
+ (1 << 22));
}
- for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
- unsigned long remainder;
- unsigned long tmprate;
- unsigned long tmpmul;
- unsigned long tmpfrac = 0;
+ /* Check if resulted rate is a valid. */
+ if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
+ return -ERANGE;
- /*
- * Calculate the multiplier associated with the current
- * divider that provide the closest rate to the requested one.
- */
- tmpmul = mult_frac(rate, tmpdiv, parent_rate);
- tmprate = mult_frac(parent_rate, tmpmul, tmpdiv);
- remainder = rate - tmprate;
+ if (update) {
+ frac->mul = nmul - 1;
+ frac->frac = nfrac;
+ }
- if (remainder) {
- tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22),
- parent_rate);
+ return tmprate;
+}
- tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate,
- tmpdiv * (1 << 22));
+static long sam9x60_frac_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
- if (tmprate > rate)
- remainder = tmprate - rate;
- else
- remainder = rate - tmprate;
- }
+ return sam9x60_frac_pll_compute_mul_frac(core, rate, *parent_rate, false);
+}
- /*
- * Compare the remainder with the best remainder found until
- * now and elect a new best multiplier/divider pair if the
- * current remainder is smaller than the best one.
- */
- if (remainder < bestremainder) {
- bestremainder = remainder;
- bestdiv = tmpdiv;
- bestmul = tmpmul;
- bestrate = tmprate;
- bestfrac = tmpfrac;
+static int sam9x60_frac_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+
+ return sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true);
+}
+
+static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ struct regmap *regmap = core->regmap;
+ unsigned long irqflags;
+ unsigned int val, cfrac, cmul;
+ long ret;
+
+ ret = sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true);
+ if (ret <= 0)
+ return ret;
+
+ spin_lock_irqsave(core->lock, irqflags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+ core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
+ cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
+ cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
+
+ if (cmul == frac->mul && cfrac == frac->frac)
+ goto unlock;
+
+ regmap_write(regmap, AT91_PMC_PLL_CTRL1,
+ (frac->mul << core->layout->mul_shift) |
+ (frac->frac << core->layout->frac_shift));
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
+ AT91_PMC_PLL_CTRL0_ENLOCK |
+ AT91_PMC_PLL_CTRL0_ENPLL);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ while (!sam9x60_pll_ready(regmap, core->id))
+ cpu_relax();
+
+unlock:
+ spin_unlock_irqrestore(core->lock, irqflags);
+
+ return ret;
+}
+
+static const struct clk_ops sam9x60_frac_pll_ops = {
+ .enable = sam9x60_frac_pll_prepare,
+ .disable = sam9x60_frac_pll_unprepare,
+ .is_enabled = sam9x60_frac_pll_is_prepared,
+ .recalc_rate = sam9x60_frac_pll_recalc_rate,
+ .round_rate = sam9x60_frac_pll_round_rate,
+ .set_rate = sam9x60_frac_pll_set_rate,
+};
+
+static const struct clk_ops sam9x60_frac_pll_ops_chg = {
+ .enable = sam9x60_frac_pll_prepare,
+ .disable = sam9x60_frac_pll_unprepare,
+ .is_enabled = sam9x60_frac_pll_is_prepared,
+ .recalc_rate = sam9x60_frac_pll_recalc_rate,
+ .round_rate = sam9x60_frac_pll_round_rate,
+ .set_rate = sam9x60_frac_pll_set_rate_chg,
+};
+
+/* This function should be called with spinlock acquired. */
+static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
+ bool enable)
+{
+ struct regmap *regmap = core->regmap;
+ u32 ena_msk = enable ? core->layout->endiv_mask : 0;
+ u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0;
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ core->layout->div_mask | ena_msk,
+ (div << core->layout->div_shift) | ena_val);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ while (!sam9x60_pll_ready(regmap, core->id))
+ cpu_relax();
+}
+
+static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
+{
+ struct sam9x60_div *div = to_sam9x60_div(core);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+ unsigned int val, cdiv;
+
+ spin_lock_irqsave(core->lock, flags);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+ cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;
+
+ /* Stop if enabled an nothing changed. */
+ if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
+ goto unlock;
+
+ sam9x60_div_pll_set_div(core, div->div, 1);
+
+unlock:
+ spin_unlock_irqrestore(core->lock, flags);
+
+ return 0;
+}
+
+static int sam9x60_div_pll_prepare(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+
+ return sam9x60_div_pll_set(core);
+}
+
+static void sam9x60_div_pll_unprepare(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ core->layout->endiv_mask, 0);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ spin_unlock_irqrestore(core->lock, flags);
+}
+
+static int sam9x60_div_pll_is_prepared(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+
+ spin_unlock_irqrestore(core->lock, flags);
+
+ return !!(val & core->layout->endiv_mask);
+}
+
+static unsigned long sam9x60_div_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, (div->div + 1));
+}
+
+static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core,
+ unsigned long *parent_rate,
+ unsigned long rate)
+{
+ const struct clk_pll_characteristics *characteristics =
+ core->characteristics;
+ struct clk_hw *parent = clk_hw_get_parent(&core->hw);
+ unsigned long tmp_rate, tmp_parent_rate, tmp_diff;
+ long best_diff = -1, best_rate = -EINVAL;
+ u32 divid;
+
+ if (!rate)
+ return 0;
+
+ if (rate < characteristics->output[0].min ||
+ rate > characteristics->output[0].max)
+ return -ERANGE;
+
+ for (divid = 1; divid < core->layout->div_mask; divid++) {
+ tmp_parent_rate = clk_hw_round_rate(parent, rate * divid);
+ if (!tmp_parent_rate)
+ continue;
+
+ tmp_rate = DIV_ROUND_CLOSEST_ULL(tmp_parent_rate, divid);
+ tmp_diff = abs(rate - tmp_rate);
+
+ if (best_diff < 0 || best_diff > tmp_diff) {
+ *parent_rate = tmp_parent_rate;
+ best_rate = tmp_rate;
+ best_diff = tmp_diff;
}
- /* We've found a perfect match! */
- if (!remainder)
+ if (!best_diff)
break;
}
- /* Check if bestrate is a valid output rate */
- if (bestrate < characteristics->output[0].min &&
- bestrate > characteristics->output[0].max)
+ if (best_rate < characteristics->output[0].min ||
+ best_rate > characteristics->output[0].max)
return -ERANGE;
- if (update) {
- pll->div = bestdiv - 1;
- pll->mul = bestmul - 1;
- pll->frac = bestfrac;
- }
+ return best_rate;
+}
- return bestrate;
+static long sam9x60_div_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+
+ return sam9x60_div_pll_compute_div(core, parent_rate, rate);
}
-static long sam9x60_pll_round_rate(struct clk *clk, unsigned long rate,
- unsigned long *parent_rate)
+static int sam9x60_div_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+
+ div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1;
- return sam9x60_pll_get_best_div_mul(pll, rate, *parent_rate, false);
+ return 0;
}
-static int sam9x60_pll_set_rate(struct clk *clk, unsigned long rate,
- unsigned long parent_rate)
+static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+ struct regmap *regmap = core->regmap;
+ unsigned long irqflags;
+ unsigned int val, cdiv;
+
+ div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1;
- return sam9x60_pll_get_best_div_mul(pll, rate, parent_rate, true);
+ spin_lock_irqsave(core->lock, irqflags);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+ core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+ cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;
+
+ /* Stop if nothing changed. */
+ if (cdiv == div->div)
+ goto unlock;
+
+ sam9x60_div_pll_set_div(core, div->div, 0);
+
+unlock:
+ spin_unlock_irqrestore(core->lock, irqflags);
+
+ return 0;
}
-static const struct clk_ops pll_ops = {
- .enable = sam9x60_pll_enable,
- .disable = sam9x60_pll_disable,
- .is_enabled = sam9x60_pll_is_enabled,
- .recalc_rate = sam9x60_pll_recalc_rate,
- .round_rate = sam9x60_pll_round_rate,
- .set_rate = sam9x60_pll_set_rate,
+static const struct clk_ops sam9x60_div_pll_ops = {
+ .enable = sam9x60_div_pll_prepare,
+ .disable = sam9x60_div_pll_unprepare,
+ .is_enabled = sam9x60_div_pll_is_prepared,
+ .recalc_rate = sam9x60_div_pll_recalc_rate,
+ .round_rate = sam9x60_div_pll_round_rate,
+ .set_rate = sam9x60_div_pll_set_rate,
};
-struct clk * __init
-sam9x60_clk_register_pll(struct regmap *regmap,
- const char *name, const char *parent_name, u8 id,
- const struct clk_pll_characteristics *characteristics)
+static const struct clk_ops sam9x60_div_pll_ops_chg = {
+ .enable = sam9x60_div_pll_prepare,
+ .disable = sam9x60_div_pll_unprepare,
+ .is_enabled = sam9x60_div_pll_is_prepared,
+ .recalc_rate = sam9x60_div_pll_recalc_rate,
+ .round_rate = sam9x60_div_pll_round_rate,
+ .set_rate = sam9x60_div_pll_set_rate_chg,
+};
+
+struct clk_hw * __init
+sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, u32 flags)
{
- struct sam9x60_pll *pll;
- unsigned int pllr;
+ struct sam9x60_frac *frac;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ unsigned long parent_rate, irqflags;
+ unsigned int val;
int ret;
- if (id > PLL_MAX_ID)
+ if (id > PLL_MAX_ID || !lock || !parent_hw)
return ERR_PTR(-EINVAL);
- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll)
+ frac = kzalloc(sizeof(*frac), GFP_KERNEL);
+ if (!frac)
return ERR_PTR(-ENOMEM);
- pll->clk.name = name;
- pll->clk.ops = &pll_ops;
- pll->parent_name = parent_name;
- pll->clk.parent_names = &pll->parent_name;
- pll->clk.num_parents = 1;
- /* pll->clk.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ if (flags & CLK_SET_RATE_GATE)
+ init.ops = &sam9x60_frac_pll_ops;
+ else
+ init.ops = &sam9x60_frac_pll_ops_chg;
+
+ init.flags = flags;
+
+ frac->core.id = id;
+ frac->core.hw.init = &init;
+ frac->core.characteristics = characteristics;
+ frac->core.layout = layout;
+ frac->core.regmap = regmap;
+ frac->core.lock = lock;
+
+ spin_lock_irqsave(frac->core.lock, irqflags);
+ if (sam9x60_pll_ready(regmap, id)) {
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
+ frac->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
+ frac->frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
+ } else {
+ /*
+ * This means the PLL is not setup by bootloaders. In this
+ * case we need to set the minimum rate for it. Otherwise
+ * a clock child of this PLL may be enabled before setting
+ * its rate leading to enabling this PLL with unsupported
+ * rate. This will lead to PLL not being locked at all.
+ */
+ parent_rate = clk_hw_get_rate(parent_hw);
+ if (!parent_rate) {
+ hw = ERR_PTR(-EINVAL);
+ goto free;
+ }
+
+ ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, FCORE_MIN,
+ parent_rate, true);
+ if (ret < 0) {
+ hw = ERR_PTR(ret);
+ goto free;
+ }
+ }
+ spin_unlock_irqrestore(frac->core.lock, irqflags);
+
+ hw = &frac->core.hw;
+ ret = clk_hw_register(NULL, hw);
+ if (ret) {
+ kfree(frac);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+
+free:
+ spin_unlock_irqrestore(frac->core.lock, irqflags);
+ kfree(frac);
+ return hw;
+}
+
+struct clk_hw * __init
+sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, u32 flags)
+{
+ struct sam9x60_div *div;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ unsigned long irqflags;
+ unsigned int val;
+ int ret;
+
+ /* We only support one changeable PLL. */
+ if (id > PLL_MAX_ID || !lock)
+ return ERR_PTR(-EINVAL);
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ if (flags & CLK_SET_RATE_GATE)
+ init.ops = &sam9x60_div_pll_ops;
+ else
+ init.ops = &sam9x60_div_pll_ops_chg;
+ init.flags = flags;
+
+ div->core.id = id;
+ div->core.hw.init = &init;
+ div->core.characteristics = characteristics;
+ div->core.layout = layout;
+ div->core.regmap = regmap;
+ div->core.lock = lock;
+
+ spin_lock_irqsave(div->core.lock, irqflags);
- pll->id = id;
- pll->characteristics = characteristics;
- pll->regmap = regmap;
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+ div->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
- regmap_write(regmap, PMC_PLL_UPDT, id);
- regmap_read(regmap, PMC_PLL_CTRL0, &pllr);
- pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr);
- regmap_read(regmap, PMC_PLL_CTRL1, &pllr);
- pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr);
+ spin_unlock_irqrestore(div->core.lock, irqflags);
- ret = clk_register(&pll->clk);
+ hw = &div->core.hw;
+ ret = clk_hw_register(NULL, hw);
if (ret) {
- kfree(pll);
- return ERR_PTR(ret);
+ kfree(div);
+ hw = ERR_PTR(ret);
}
- return &pll->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c
index 960678db1c..3a070d0d34 100644
--- a/drivers/clk/at91/clk-slow.c
+++ b/drivers/clk/at91/clk-slow.c
@@ -5,28 +5,25 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
struct clk_sam9260_slow {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
- const char *parent_names[2];
};
-#define to_clk_sam9260_slow(clk) container_of(clk, struct clk_sam9260_slow, clk)
+#define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw)
-static int clk_sam9260_slow_get_parent(struct clk *clk)
+static int clk_sam9260_slow_get_parent(struct clk_hw *hw)
{
- struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(clk);
+ struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw);
unsigned int status;
regmap_read(slowck->regmap, AT91_PMC_SR, &status);
@@ -38,13 +35,15 @@ static const struct clk_ops sam9260_slow_ops = {
.get_parent = clk_sam9260_slow_get_parent,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_sam9260_slow(struct regmap *regmap,
const char *name,
const char **parent_names,
int num_parents)
{
struct clk_sam9260_slow *slowck;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (!name)
@@ -53,20 +52,25 @@ at91_clk_register_sam9260_slow(struct regmap *regmap,
if (!parent_names || !num_parents)
return ERR_PTR(-EINVAL);
- slowck = xzalloc(sizeof(*slowck));
- slowck->clk.name = name;
- slowck->clk.ops = &sam9260_slow_ops;
- memcpy(slowck->parent_names, parent_names,
- num_parents * sizeof(slowck->parent_names[0]));
- slowck->clk.parent_names = slowck->parent_names;
- slowck->clk.num_parents = num_parents;
+ slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
+ if (!slowck)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &sam9260_slow_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = 0;
+
+ slowck->hw.init = &init;
slowck->regmap = regmap;
- ret = clk_register(&slowck->clk);
+ hw = &slowck->hw;
+ ret = clk_hw_register(NULL, &slowck->hw);
if (ret) {
kfree(slowck);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &slowck->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c
index 0027ebc8bb..dc1b150750 100644
--- a/drivers/clk/at91/clk-smd.c
+++ b/drivers/clk/at91/clk-smd.c
@@ -3,35 +3,30 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define SMD_SOURCE_MAX 2
-
#define SMD_DIV_SHIFT 8
#define SMD_MAX_DIV 0xf
struct at91sam9x5_clk_smd {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
- const char *parent_names[SMD_SOURCE_MAX];
};
-#define to_at91sam9x5_clk_smd(clk) \
- container_of(clk, struct at91sam9x5_clk_smd, clk)
+#define to_at91sam9x5_clk_smd(hw) \
+ container_of(hw, struct at91sam9x5_clk_smd, hw)
-static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk *clk,
+static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+ struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
unsigned int smdr;
u8 smddiv;
@@ -41,7 +36,7 @@ static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk *clk,
return parent_rate / (smddiv + 1);
}
-static long at91sam9x5_clk_smd_round_rate(struct clk *clk, unsigned long rate,
+static long at91sam9x5_clk_smd_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long div;
@@ -63,22 +58,22 @@ static long at91sam9x5_clk_smd_round_rate(struct clk *clk, unsigned long rate,
return bestrate;
}
-static int at91sam9x5_clk_smd_set_parent(struct clk *clk, u8 index)
+static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index)
{
- struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+ struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
if (index > 1)
return -EINVAL;
- regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS,
- index ? AT91_PMC_SMDS : 0);
+ regmap_update_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS,
+ index ? AT91_PMC_SMDS : 0);
return 0;
}
-static int at91sam9x5_clk_smd_get_parent(struct clk *clk)
+static int at91sam9x5_clk_smd_get_parent(struct clk_hw *hw)
{
- struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+ struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
unsigned int smdr;
regmap_read(smd->regmap, AT91_PMC_SMD, &smdr);
@@ -86,17 +81,17 @@ static int at91sam9x5_clk_smd_get_parent(struct clk *clk)
return smdr & AT91_PMC_SMDS;
}
-static int at91sam9x5_clk_smd_set_rate(struct clk *clk, unsigned long rate,
+static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+ struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(hw);
unsigned long div = parent_rate / rate;
if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1))
return -EINVAL;
- regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV,
- (div - 1) << SMD_DIV_SHIFT);
+ regmap_update_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV,
+ (div - 1) << SMD_DIV_SHIFT);
return 0;
}
@@ -109,28 +104,34 @@ static const struct clk_ops at91sam9x5_smd_ops = {
.set_rate = at91sam9x5_clk_smd_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents)
{
struct at91sam9x5_clk_smd *smd;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
- smd = xzalloc(sizeof(*smd));
- smd->clk.name = name;
- smd->clk.ops = &at91sam9x5_smd_ops;
- memcpy(smd->parent_names, parent_names,
- num_parents * sizeof(smd->parent_names[0]));
- smd->clk.parent_names = smd->parent_names;
- smd->clk.num_parents = num_parents;
- /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
+ smd = kzalloc(sizeof(*smd), GFP_KERNEL);
+ if (!smd)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &at91sam9x5_smd_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+ smd->hw.init = &init;
smd->regmap = regmap;
- ret = clk_register(&smd->clk);
+ hw = &smd->hw;
+ ret = clk_hw_register(NULL, &smd->hw);
if (ret) {
kfree(smd);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &smd->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 77f0dff98b..5f367e292a 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -2,14 +2,13 @@
/*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
@@ -17,12 +16,12 @@
#define SYSTEM_MAX_NAME_SZ 32
-#define to_clk_system(clk) container_of(clk, struct clk_system, clk)
+#define to_clk_system(hw) container_of(hw, struct clk_system, hw)
struct clk_system {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
+ struct at91_clk_pms pms;
u8 id;
- const char *parent_name;
};
static inline int is_pck(int id)
@@ -36,12 +35,12 @@ static inline bool clk_system_ready(struct regmap *regmap, int id)
regmap_read(regmap, AT91_PMC_SR, &status);
- return status & (1 << id) ? 1 : 0;
+ return !!(status & (1 << id));
}
-static int clk_system_enable(struct clk *clk)
+static int clk_system_prepare(struct clk_hw *hw)
{
- struct clk_system *sys = to_clk_system(clk);
+ struct clk_system *sys = to_clk_system(hw);
regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id);
@@ -49,21 +48,21 @@ static int clk_system_enable(struct clk *clk)
return 0;
while (!clk_system_ready(sys->regmap, sys->id))
- barrier();
+ cpu_relax();
return 0;
}
-static void clk_system_disable(struct clk *clk)
+static void clk_system_unprepare(struct clk_hw *hw)
{
- struct clk_system *sys = to_clk_system(clk);
+ struct clk_system *sys = to_clk_system(hw);
regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id);
}
-static int clk_system_is_enabled(struct clk *clk)
+static int clk_system_is_prepared(struct clk_hw *hw)
{
- struct clk_system *sys = to_clk_system(clk);
+ struct clk_system *sys = to_clk_system(hw);
unsigned int status;
regmap_read(sys->regmap, AT91_PMC_SCSR, &status);
@@ -76,40 +75,47 @@ static int clk_system_is_enabled(struct clk *clk)
regmap_read(sys->regmap, AT91_PMC_SR, &status);
- return status & (1 << sys->id) ? 1 : 0;
+ return !!(status & (1 << sys->id));
}
static const struct clk_ops system_ops = {
- .enable = clk_system_enable,
- .disable = clk_system_disable,
- .is_enabled = clk_system_is_enabled,
+ .enable = clk_system_prepare,
+ .disable = clk_system_unprepare,
+ .is_enabled = clk_system_is_prepared,
};
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_system(struct regmap *regmap, const char *name,
- const char *parent_name, u8 id)
+ const char *parent_name, u8 id, unsigned long flags)
{
struct clk_system *sys;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (!parent_name || id > SYSTEM_MAX_ID)
return ERR_PTR(-EINVAL);
- sys = xzalloc(sizeof(*sys));
- sys->clk.name = name;
- sys->clk.ops = &system_ops;
- sys->parent_name = parent_name;
- sys->clk.parent_names = &sys->parent_name;
- sys->clk.num_parents = 1;
- /* init.flags = CLK_SET_RATE_PARENT; */
+ sys = kzalloc(sizeof(*sys), GFP_KERNEL);
+ if (!sys)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &system_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT | flags;
+
sys->id = id;
+ sys->hw.init = &init;
sys->regmap = regmap;
- ret = clk_register(&sys->clk);
+ hw = &sys->hw;
+ ret = clk_hw_register(NULL, &sys->hw);
if (ret) {
kfree(sys);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &sys->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index 2cf68593c0..4473dc7c34 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -3,19 +3,15 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
+#include <linux/regmap.h>
#include "pmc.h"
-#define USB_SOURCE_MAX 2
-
#define SAM9X5_USB_DIV_SHIFT 8
#define SAM9X5_USB_MAX_DIV 0xf
@@ -26,29 +22,29 @@
#define SAM9X60_USBS_MASK GENMASK(1, 0)
struct at91sam9x5_clk_usb {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
- const char *parent_names[USB_SOURCE_MAX];
+ struct at91_clk_pms pms;
u32 usbs_mask;
+ u8 num_parents;
};
-#define to_at91sam9x5_clk_usb(clk) \
- container_of(clk, struct at91sam9x5_clk_usb, clk)
+#define to_at91sam9x5_clk_usb(hw) \
+ container_of(hw, struct at91sam9x5_clk_usb, hw)
struct at91rm9200_clk_usb {
- struct clk clk;
+ struct clk_hw hw;
struct regmap *regmap;
u32 divisors[4];
- const char *parent_name;
};
-#define to_at91rm9200_clk_usb(clk) \
- container_of(clk, struct at91rm9200_clk_usb, clk)
+#define to_at91rm9200_clk_usb(hw) \
+ container_of(hw, struct at91rm9200_clk_usb, hw)
-static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk,
+static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
unsigned int usbr;
u8 usbdiv;
@@ -58,21 +54,21 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk,
return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
}
-static int at91sam9x5_clk_usb_set_parent(struct clk *clk, u8 index)
+static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
{
- struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
- if (index > 1)
+ if (index >= usb->num_parents)
return -EINVAL;
- regmap_write_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
+ regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
return 0;
}
-static int at91sam9x5_clk_usb_get_parent(struct clk *clk)
+static int at91sam9x5_clk_usb_get_parent(struct clk_hw *hw)
{
- struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
unsigned int usbr;
regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
@@ -80,10 +76,10 @@ static int at91sam9x5_clk_usb_get_parent(struct clk *clk)
return usbr & usb->usbs_mask;
}
-static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate,
+static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
- struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
unsigned long div;
if (!rate)
@@ -93,8 +89,8 @@ static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate,
if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
return -EINVAL;
- regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
- (div - 1) << SAM9X5_USB_DIV_SHIFT);
+ regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
+ (div - 1) << SAM9X5_USB_DIV_SHIFT);
return 0;
}
@@ -106,26 +102,26 @@ static const struct clk_ops at91sam9x5_usb_ops = {
.set_rate = at91sam9x5_clk_usb_set_rate,
};
-static int at91sam9n12_clk_usb_enable(struct clk *clk)
+static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
{
- struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
- regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
- AT91_PMC_USBS);
+ regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
+ AT91_PMC_USBS);
return 0;
}
-static void at91sam9n12_clk_usb_disable(struct clk *clk)
+static void at91sam9n12_clk_usb_disable(struct clk_hw *hw)
{
- struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
- regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
+ regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
}
-static int at91sam9n12_clk_usb_is_enabled(struct clk *clk)
+static int at91sam9n12_clk_usb_is_enabled(struct clk_hw *hw)
{
- struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
unsigned int usbr;
regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
@@ -141,37 +137,43 @@ static const struct clk_ops at91sam9n12_usb_ops = {
.set_rate = at91sam9x5_clk_usb_set_rate,
};
-static struct clk * __init
+static struct clk_hw * __init
_at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents,
u32 usbs_mask)
{
struct at91sam9x5_clk_usb *usb;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
- usb->clk.name = name;
- usb->clk.ops = &at91sam9x5_usb_ops;
- memcpy(usb->parent_names, parent_names,
- num_parents * sizeof(usb->parent_names[0]));
- usb->clk.parent_names = usb->parent_names;
- usb->clk.num_parents = num_parents;
- usb->clk.flags = CLK_SET_RATE_PARENT;
- /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | */
- /* CLK_SET_RATE_PARENT; */
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &at91sam9x5_usb_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+
+ usb->hw.init = &init;
usb->regmap = regmap;
- usb->usbs_mask = SAM9X5_USBS_MASK;
+ usb->usbs_mask = usbs_mask;
+ usb->num_parents = num_parents;
- ret = clk_register(&usb->clk);
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
if (ret) {
kfree(usb);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &usb->clk;
+ return hw;
}
-struct clk * __init
+struct clk_hw * __init
at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents)
{
@@ -179,7 +181,7 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
num_parents, SAM9X5_USBS_MASK);
}
-struct clk * __init
+struct clk_hw * __init
sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents)
{
@@ -187,35 +189,42 @@ sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
num_parents, SAM9X60_USBS_MASK);
}
-struct clk * __init
+struct clk_hw * __init
at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct at91sam9x5_clk_usb *usb;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
- usb = xzalloc(sizeof(*usb));
- usb->clk.name = name;
- usb->clk.ops = &at91sam9n12_usb_ops;
- usb->parent_names[0] = parent_name;
- usb->clk.parent_names = &usb->parent_names[0];
- usb->clk.num_parents = 1;
- /* init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; */
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &at91sam9n12_usb_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
+
+ usb->hw.init = &init;
usb->regmap = regmap;
- ret = clk_register(&usb->clk);
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
if (ret) {
kfree(usb);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &usb->clk;
+ return hw;
}
-static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk,
+static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
+ struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
unsigned int pllbr;
u8 usbdiv;
@@ -228,11 +237,11 @@ static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk,
return 0;
}
-static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate,
+static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
- struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
- struct clk *parent = clk_get_parent(clk);
+ struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
+ struct clk_hw *parent = clk_hw_get_parent(hw);
unsigned long bestrate = 0;
int bestdiff = -1;
unsigned long tmprate;
@@ -246,7 +255,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate,
continue;
tmp_parent_rate = rate * usb->divisors[i];
- tmp_parent_rate = clk_round_rate(parent, tmp_parent_rate);
+ tmp_parent_rate = clk_hw_round_rate(parent, tmp_parent_rate);
tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
if (tmprate < rate)
tmpdiff = rate - tmprate;
@@ -266,11 +275,11 @@ static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate,
return bestrate;
}
-static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate,
+static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
int i;
- struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
+ struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
unsigned long div;
if (!rate)
@@ -280,9 +289,9 @@ static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate,
for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
if (usb->divisors[i] == div) {
- regmap_write_bits(usb->regmap, AT91_CKGR_PLLBR,
- AT91_PMC_USBDIV,
- i << RM9200_USB_DIV_SHIFT);
+ regmap_update_bits(usb->regmap, AT91_CKGR_PLLBR,
+ AT91_PMC_USBDIV,
+ i << RM9200_USB_DIV_SHIFT);
return 0;
}
@@ -297,29 +306,35 @@ static const struct clk_ops at91rm9200_usb_ops = {
.set_rate = at91rm9200_clk_usb_set_rate,
};
-struct clk * __init
+struct clk_hw * __init
at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name, const u32 *divisors)
{
struct at91rm9200_clk_usb *usb;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
- usb = xzalloc(sizeof(*usb));
- usb->clk.name = name;
- usb->clk.ops = &at91rm9200_usb_ops;
- usb->parent_name = parent_name;
- usb->clk.parent_names = &usb->parent_name;
- usb->clk.num_parents = 1;
- /* init.flags = CLK_SET_RATE_PARENT; */
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &at91rm9200_usb_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT;
+ usb->hw.init = &init;
usb->regmap = regmap;
memcpy(usb->divisors, divisors, sizeof(usb->divisors));
- ret = clk_register(&usb->clk);
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
if (ret) {
kfree(usb);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &usb->clk;
+ return hw;
}
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index 3d71cd615f..7d85e43024 100644
--- a/drivers/clk/at91/clk-utmi.c
+++ b/drivers/clk/at91/clk-utmi.c
@@ -3,15 +3,14 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
-#include <clock.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
+#include <of.h>
#include <mfd/syscon.h>
-#include <regmap.h>
-
+#include <linux/regmap.h>
#include <soc/at91/atmel-sfr.h>
+#include <linux/printk.h>
#include "pmc.h"
@@ -19,16 +18,16 @@
* The purpose of this clock is to generate a 480 MHz signal. A different
* rate can't be configured.
*/
-#define UTMI_RATE 480000000
+#define UTMI_RATE 480000000
struct clk_utmi {
- struct clk clk;
- const char *parent;
+ struct clk_hw hw;
struct regmap *regmap_pmc;
struct regmap *regmap_sfr;
+ struct at91_clk_pms pms;
};
-#define to_clk_utmi(clk) container_of(clk, struct clk_utmi, clk)
+#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
static inline bool clk_utmi_ready(struct regmap *regmap)
{
@@ -39,10 +38,10 @@ static inline bool clk_utmi_ready(struct regmap *regmap)
return status & AT91_PMC_LOCKU;
}
-static int clk_utmi_enable(struct clk *clk)
+static int clk_utmi_prepare(struct clk_hw *hw)
{
- struct clk *hw_parent;
- struct clk_utmi *utmi = to_clk_utmi(clk);
+ struct clk_hw *hw_parent;
+ struct clk_utmi *utmi = to_clk_utmi(hw);
unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
AT91_PMC_BIASEN;
unsigned int utmi_ref_clk_freq;
@@ -53,8 +52,8 @@ static int clk_utmi_enable(struct clk *clk)
* FREQ field of the SFR_UTMICKTRIM register to generate properly
* the utmi clock.
*/
- hw_parent = clk_get_parent(clk);
- parent_rate = clk_get_rate(hw_parent);
+ hw_parent = clk_hw_get_parent(hw);
+ parent_rate = clk_hw_get_rate(hw_parent);
switch (parent_rate) {
case 12000000:
@@ -78,80 +77,173 @@ static int clk_utmi_enable(struct clk *clk)
return -EINVAL;
}
-
if (utmi->regmap_sfr) {
- regmap_write_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
- AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
+ regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
+ AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
} else if (utmi_ref_clk_freq) {
pr_err("UTMICK: sfr node required\n");
return -EINVAL;
}
- regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);
+ regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);
while (!clk_utmi_ready(utmi->regmap_pmc))
- barrier();
+ cpu_relax();
return 0;
}
-static int clk_utmi_is_enabled(struct clk *clk)
+static int clk_utmi_is_prepared(struct clk_hw *hw)
{
- struct clk_utmi *utmi = to_clk_utmi(clk);
+ struct clk_utmi *utmi = to_clk_utmi(hw);
return clk_utmi_ready(utmi->regmap_pmc);
}
-static void clk_utmi_disable(struct clk *clk)
+static void clk_utmi_unprepare(struct clk_hw *hw)
{
- struct clk_utmi *utmi = to_clk_utmi(clk);
+ struct clk_utmi *utmi = to_clk_utmi(hw);
- regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,
- AT91_PMC_UPLLEN, 0);
+ regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,
+ AT91_PMC_UPLLEN, 0);
}
-static unsigned long clk_utmi_recalc_rate(struct clk *clk,
+static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- /* UTMI clk rate is fixed */
+ /* UTMI clk rate is fixed. */
return UTMI_RATE;
}
static const struct clk_ops utmi_ops = {
- .enable = clk_utmi_enable,
- .disable = clk_utmi_disable,
- .is_enabled = clk_utmi_is_enabled,
+ .enable = clk_utmi_prepare,
+ .disable = clk_utmi_unprepare,
+ .is_enabled = clk_utmi_is_prepared,
.recalc_rate = clk_utmi_recalc_rate,
};
-struct clk * __init
-at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
- const char *name, const char *parent_name)
+static struct clk_hw * __init
+at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
+ struct regmap *regmap_sfr,
+ const char *name, const char *parent_name,
+ const struct clk_ops *ops, unsigned long flags)
{
- int ret;
struct clk_utmi *utmi;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
- utmi = xzalloc(sizeof(*utmi));
-
- utmi->clk.name = name;
- utmi->clk.ops = &utmi_ops;
-
- if (parent_name) {
- utmi->parent = parent_name;
- utmi->clk.parent_names = &utmi->parent;
- utmi->clk.num_parents = 1;
- }
+ utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
+ if (!utmi)
+ return ERR_PTR(-ENOMEM);
- /* utmi->clk.flags = CLK_SET_RATE_GATE; */
+ init.name = name;
+ init.ops = ops;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.flags = flags;
+ utmi->hw.init = &init;
utmi->regmap_pmc = regmap_pmc;
utmi->regmap_sfr = regmap_sfr;
- ret = clk_register(&utmi->clk);
+ hw = &utmi->hw;
+ ret = clk_hw_register(NULL, &utmi->hw);
if (ret) {
kfree(utmi);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &utmi->clk;
+ return hw;
+}
+
+struct clk_hw * __init
+at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
+ const char *name, const char *parent_name)
+{
+ return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
+ parent_name, &utmi_ops, CLK_SET_RATE_GATE);
+}
+
+static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
+{
+ struct clk_utmi *utmi = to_clk_utmi(hw);
+ struct clk_hw *hw_parent;
+ unsigned long parent_rate;
+ unsigned int val;
+
+ hw_parent = clk_hw_get_parent(hw);
+ parent_rate = clk_hw_get_rate(hw_parent);
+
+ switch (parent_rate) {
+ case 16000000:
+ val = 0;
+ break;
+ case 20000000:
+ val = 2;
+ break;
+ case 24000000:
+ val = 3;
+ break;
+ case 32000000:
+ val = 5;
+ break;
+ default:
+ pr_err("UTMICK: unsupported main_xtal rate\n");
+ return -EINVAL;
+ }
+
+ regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);
+
+ return 0;
+
+}
+
+static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
+{
+ struct clk_utmi *utmi = to_clk_utmi(hw);
+ struct clk_hw *hw_parent;
+ unsigned long parent_rate;
+ unsigned int val;
+
+ hw_parent = clk_hw_get_parent(hw);
+ parent_rate = clk_hw_get_rate(hw_parent);
+
+ regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);
+ switch (val & 0x7) {
+ case 0:
+ if (parent_rate == 16000000)
+ return 1;
+ break;
+ case 2:
+ if (parent_rate == 20000000)
+ return 1;
+ break;
+ case 3:
+ if (parent_rate == 24000000)
+ return 1;
+ break;
+ case 5:
+ if (parent_rate == 32000000)
+ return 1;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct clk_ops sama7g5_utmi_ops = {
+ .enable = clk_utmi_sama7g5_prepare,
+ .is_enabled = clk_utmi_sama7g5_is_prepared,
+ .recalc_rate = clk_utmi_recalc_rate,
+};
+
+struct clk_hw * __init
+at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
+ const char *parent_name)
+{
+ return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
+ parent_name, &sama7g5_utmi_ops, 0);
}
diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c
deleted file mode 100644
index b888249199..0000000000
--- a/drivers/clk/at91/dt-compat.c
+++ /dev/null
@@ -1,727 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <of.h>
-#include <driver.h>
-#include <regmap.h>
-#include <mfd/syscon.h>
-
-
-#include "pmc.h"
-
-#define MASTER_SOURCE_MAX 4
-
-#define PERIPHERAL_AT91RM9200 0
-#define PERIPHERAL_AT91SAM9X5 1
-
-#define PERIPHERAL_MAX 64
-
-#define PERIPHERAL_ID_MIN 2
-
-#define PROG_SOURCE_MAX 5
-#define PROG_ID_MAX 7
-
-#define SYSTEM_MAX_ID 31
-
-static const struct clk_pcr_layout dt_pcr_layout = {
- .offset = 0x10c,
- .cmd = BIT(12),
- .pid_mask = GENMASK(5, 0),
- .div_mask = GENMASK(17, 16),
- .gckcss_mask = GENMASK(10, 8),
-};
-
-static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
-{
- struct clk *hw;
- const char *name = np->name;
- const char *parent_name;
- struct regmap *regmap;
- bool bypass;
-
- of_property_read_string(np, "clock-output-names", &name);
- bypass = of_property_read_bool(np, "atmel,osc-bypass");
- parent_name = of_clk_get_parent_name(np, 0);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- hw = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc",
- of_at91rm9200_clk_main_osc_setup);
-
-static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
-{
- struct clk *hw;
- u32 frequency = 0;
- u32 accuracy = 0;
- const char *name = np->name;
- struct regmap *regmap;
-
- of_property_read_string(np, "clock-output-names", &name);
- of_property_read_u32(np, "clock-frequency", &frequency);
- of_property_read_u32(np, "clock-accuracy", &accuracy);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- hw = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc",
- of_at91sam9x5_clk_main_rc_osc_setup);
-
-static void __init of_at91rm9200_clk_main_setup(struct device_node *np)
-{
- struct clk *hw;
- const char *parent_name;
- const char *name = np->name;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- hw = at91_clk_register_rm9200_main(regmap, name, parent_name);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main",
- of_at91rm9200_clk_main_setup);
-
-static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
-{
- struct clk *hw;
- const char *parent_names[2];
- unsigned int num_parents;
- const char *name = np->name;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > 2)
- return;
-
- of_clk_parent_fill(np, parent_names, num_parents);
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- of_property_read_string(np, "clock-output-names", &name);
-
- hw = at91_clk_register_sam9x5_main(regmap, name, parent_names,
- num_parents);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main",
- of_at91sam9x5_clk_main_setup);
-
-static struct clk_master_characteristics * __init
-of_at91_clk_master_get_characteristics(struct device_node *np)
-{
- struct clk_master_characteristics *characteristics;
-
- characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
- if (!characteristics)
- return NULL;
-
- if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output))
- goto out_free_characteristics;
-
- of_property_read_u32_array(np, "atmel,clk-divisors",
- characteristics->divisors, 4);
-
- characteristics->have_div3_pres =
- of_property_read_bool(np, "atmel,master-clk-have-div3-pres");
-
- return characteristics;
-
-out_free_characteristics:
- kfree(characteristics);
- return NULL;
-}
-
-static void __init
-of_at91_clk_master_setup(struct device_node *np,
- const struct clk_master_layout *layout)
-{
- struct clk *hw;
- unsigned int num_parents;
- const char *parent_names[MASTER_SOURCE_MAX];
- const char *name = np->name;
- struct clk_master_characteristics *characteristics;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX)
- return;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- characteristics = of_at91_clk_master_get_characteristics(np);
- if (!characteristics)
- return;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- hw = at91_clk_register_master(regmap, name, num_parents,
- parent_names, layout,
- characteristics);
- if (IS_ERR(hw))
- goto out_free_characteristics;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
- return;
-
-out_free_characteristics:
- kfree(characteristics);
-}
-
-static void __init of_at91rm9200_clk_master_setup(struct device_node *np)
-{
- of_at91_clk_master_setup(np, &at91rm9200_master_layout);
-}
-CLK_OF_DECLARE(at91rm9200_clk_master, "atmel,at91rm9200-clk-master",
- of_at91rm9200_clk_master_setup);
-
-static void __init of_at91sam9x5_clk_master_setup(struct device_node *np)
-{
- of_at91_clk_master_setup(np, &at91sam9x5_master_layout);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master",
- of_at91sam9x5_clk_master_setup);
-
-static void __init
-of_at91_clk_periph_setup(struct device_node *np, u8 type)
-{
- int num;
- u32 id;
- struct clk *hw;
- const char *parent_name;
- const char *name;
- struct device_node *periphclknp;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- return;
-
- num = of_get_child_count(np);
- if (!num || num > PERIPHERAL_MAX)
- return;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- for_each_child_of_node(np, periphclknp) {
- if (of_property_read_u32(periphclknp, "reg", &id))
- continue;
-
- if (id >= PERIPHERAL_MAX)
- continue;
-
- if (of_property_read_string(np, "clock-output-names", &name))
- name = periphclknp->name;
-
- if (type == PERIPHERAL_AT91RM9200) {
- hw = at91_clk_register_peripheral(regmap, name,
- parent_name, id);
- } else {
- struct clk_range range = CLK_RANGE(0, 0);
-
- of_at91_get_clk_range(periphclknp,
- "atmel,clk-output-range",
- &range);
-
- hw = at91_clk_register_sam9x5_peripheral(regmap,
- &dt_pcr_layout,
- name,
- parent_name,
- id, &range);
- }
-
- if (IS_ERR(hw))
- continue;
-
- of_clk_add_provider(periphclknp, of_clk_src_simple_get, hw);
- }
-}
-
-static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
-{
- of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
-}
-CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
- of_at91rm9200_clk_periph_setup);
-
-static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
-{
- of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
- of_at91sam9x5_clk_periph_setup);
-
-static struct clk_pll_characteristics * __init
-of_at91_clk_pll_get_characteristics(struct device_node *np)
-{
- int i;
- int offset;
- u32 tmp;
- int num_output;
- u32 num_cells;
- struct clk_range input;
- struct clk_range *output;
- u8 *out = NULL;
- u16 *icpll = NULL;
- struct clk_pll_characteristics *characteristics;
-
- if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input))
- return NULL;
-
- if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells",
- &num_cells))
- return NULL;
-
- if (num_cells < 2 || num_cells > 4)
- return NULL;
-
- if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp))
- return NULL;
- num_output = tmp / (sizeof(u32) * num_cells);
-
- characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
- if (!characteristics)
- return NULL;
-
- output = kcalloc(num_output, sizeof(*output), GFP_KERNEL);
- if (!output)
- goto out_free_characteristics;
-
- if (num_cells > 2) {
- out = kcalloc(num_output, sizeof(*out), GFP_KERNEL);
- if (!out)
- goto out_free_output;
- }
-
- if (num_cells > 3) {
- icpll = kcalloc(num_output, sizeof(*icpll), GFP_KERNEL);
- if (!icpll)
- goto out_free_output;
- }
-
- for (i = 0; i < num_output; i++) {
- offset = i * num_cells;
- if (of_property_read_u32_index(np,
- "atmel,pll-clk-output-ranges",
- offset, &tmp))
- goto out_free_output;
- output[i].min = tmp;
- if (of_property_read_u32_index(np,
- "atmel,pll-clk-output-ranges",
- offset + 1, &tmp))
- goto out_free_output;
- output[i].max = tmp;
-
- if (num_cells == 2)
- continue;
-
- if (of_property_read_u32_index(np,
- "atmel,pll-clk-output-ranges",
- offset + 2, &tmp))
- goto out_free_output;
- out[i] = tmp;
-
- if (num_cells == 3)
- continue;
-
- if (of_property_read_u32_index(np,
- "atmel,pll-clk-output-ranges",
- offset + 3, &tmp))
- goto out_free_output;
- icpll[i] = tmp;
- }
-
- characteristics->input = input;
- characteristics->num_output = num_output;
- characteristics->output = output;
- characteristics->out = out;
- characteristics->icpll = icpll;
- return characteristics;
-
-out_free_output:
- kfree(icpll);
- kfree(out);
- kfree(output);
-out_free_characteristics:
- kfree(characteristics);
- return NULL;
-}
-
-static void __init
-of_at91_clk_pll_setup(struct device_node *np,
- const struct clk_pll_layout *layout)
-{
- u32 id;
- struct clk *hw;
- struct regmap *regmap;
- const char *parent_name;
- const char *name = np->name;
- struct clk_pll_characteristics *characteristics;
-
- if (of_property_read_u32(np, "reg", &id))
- return;
-
- parent_name = of_clk_get_parent_name(np, 0);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- characteristics = of_at91_clk_pll_get_characteristics(np);
- if (!characteristics)
- return;
-
- hw = at91_clk_register_pll(regmap, name, parent_name, id, layout,
- characteristics);
- if (IS_ERR(hw))
- goto out_free_characteristics;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
- return;
-
-out_free_characteristics:
- kfree(characteristics);
-}
-
-static void __init of_at91rm9200_clk_pll_setup(struct device_node *np)
-{
- of_at91_clk_pll_setup(np, &at91rm9200_pll_layout);
-}
-CLK_OF_DECLARE(at91rm9200_clk_pll, "atmel,at91rm9200-clk-pll",
- of_at91rm9200_clk_pll_setup);
-
-static void __init of_sama5d3_clk_pll_setup(struct device_node *np)
-{
- of_at91_clk_pll_setup(np, &sama5d3_pll_layout);
-}
-CLK_OF_DECLARE(sama5d3_clk_pll, "atmel,sama5d3-clk-pll",
- of_sama5d3_clk_pll_setup);
-
-static void __init
-of_at91sam9x5_clk_plldiv_setup(struct device_node *np)
-{
- struct clk *hw;
- const char *parent_name;
- const char *name = np->name;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- hw = at91_clk_register_plldiv(regmap, name, parent_name);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",
- of_at91sam9x5_clk_plldiv_setup);
-
-static void __init
-of_at91_clk_prog_setup(struct device_node *np,
- const struct clk_programmable_layout *layout)
-{
- int num;
- u32 id;
- struct clk *hw;
- unsigned int num_parents;
- const char *parent_names[PROG_SOURCE_MAX];
- const char *name;
- struct device_node *progclknp;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > PROG_SOURCE_MAX)
- return;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- num = of_get_child_count(np);
- if (!num || num > (PROG_ID_MAX + 1))
- return;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- for_each_child_of_node(np, progclknp) {
- if (of_property_read_u32(progclknp, "reg", &id))
- continue;
-
- if (of_property_read_string(np, "clock-output-names", &name))
- name = progclknp->name;
-
- hw = at91_clk_register_programmable(regmap, name,
- parent_names, num_parents,
- id, layout);
- if (IS_ERR(hw))
- continue;
-
- of_clk_add_provider(progclknp, of_clk_src_simple_get, hw);
- }
-}
-
-static void __init of_at91rm9200_clk_prog_setup(struct device_node *np)
-{
- of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout);
-}
-CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
- of_at91rm9200_clk_prog_setup);
-
-static void __init of_at91sam9g45_clk_prog_setup(struct device_node *np)
-{
- of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout);
-}
-CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
- of_at91sam9g45_clk_prog_setup);
-
-static void __init of_at91sam9x5_clk_prog_setup(struct device_node *np)
-{
- of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
- of_at91sam9x5_clk_prog_setup);
-
-#ifdef CONFIG_HAVE_AT91_SMD
-#define SMD_SOURCE_MAX 2
-
-static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np)
-{
- struct clk *hw;
- unsigned int num_parents;
- const char *parent_names[SMD_SOURCE_MAX];
- const char *name = np->name;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > SMD_SOURCE_MAX)
- return;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- hw = at91sam9x5_clk_register_smd(regmap, name, parent_names,
- num_parents);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd",
- of_at91sam9x5_clk_smd_setup);
-#endif /* CONFIG_HAVE_AT91_SMD */
-
-static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
-{
- int num;
- u32 id;
- struct clk *hw;
- const char *name;
- struct device_node *sysclknp;
- const char *parent_name;
- struct regmap *regmap;
-
- num = of_get_child_count(np);
- if (num > (SYSTEM_MAX_ID + 1))
- return;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- for_each_child_of_node(np, sysclknp) {
- if (of_property_read_u32(sysclknp, "reg", &id))
- continue;
-
- if (of_property_read_string(np, "clock-output-names", &name))
- name = sysclknp->name;
-
- parent_name = of_clk_get_parent_name(sysclknp, 0);
-
- hw = at91_clk_register_system(regmap, name, parent_name, id);
- if (IS_ERR(hw))
- continue;
-
- of_clk_add_provider(sysclknp, of_clk_src_simple_get, hw);
- }
-}
-CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system",
- of_at91rm9200_clk_sys_setup);
-
-#ifdef CONFIG_HAVE_AT91_USB_CLK
-#define USB_SOURCE_MAX 2
-
-static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np)
-{
- struct clk *hw;
- unsigned int num_parents;
- const char *parent_names[USB_SOURCE_MAX];
- const char *name = np->name;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > USB_SOURCE_MAX)
- return;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- hw = at91sam9x5_clk_register_usb(regmap, name, parent_names,
- num_parents);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb",
- of_at91sam9x5_clk_usb_setup);
-
-static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np)
-{
- struct clk *hw;
- const char *parent_name;
- const char *name = np->name;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- return;
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- hw = at91sam9n12_clk_register_usb(regmap, name, parent_name);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb",
- of_at91sam9n12_clk_usb_setup);
-
-static void __init of_at91rm9200_clk_usb_setup(struct device_node *np)
-{
- struct clk *hw;
- const char *parent_name;
- const char *name = np->name;
- u32 divisors[4] = {0, 0, 0, 0};
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- return;
-
- of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4);
- if (!divisors[0])
- return;
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
- hw = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb",
- of_at91rm9200_clk_usb_setup);
-#endif /* CONFIG_HAVE_AT91_USB_CLK */
-
-#ifdef CONFIG_HAVE_AT91_UTMI
-static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
-{
- struct clk *hw;
- const char *parent_name;
- const char *name = np->name;
- struct regmap *regmap_pmc, *regmap_sfr;
-
- parent_name = of_clk_get_parent_name(np, 0);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap_pmc = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap_pmc))
- return;
-
- /*
- * If the device supports different mainck rates, this value has to be
- * set in the UTMI Clock Trimming register.
- * - 9x5: mainck supports several rates but it is indicated that a
- * 12 MHz is needed in case of USB.
- * - sama5d3 and sama5d2: mainck supports several rates. Configuring
- * the FREQ field of the UTMI Clock Trimming register is mandatory.
- * - sama5d4: mainck is at 12 MHz.
- *
- * We only need to retrieve sama5d3 or sama5d2 sfr regmap.
- */
- regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d3-sfr");
- if (IS_ERR(regmap_sfr)) {
- regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
- if (IS_ERR(regmap_sfr))
- regmap_sfr = NULL;
- }
-
- hw = at91_clk_register_utmi(regmap_pmc, regmap_sfr, name, parent_name);
- if (IS_ERR(hw))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi",
- of_at91sam9x5_clk_utmi_setup);
-#endif /* CONFIG_HAVE_AT91_UTMI */
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 171b62cbfd..4780b5790d 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -3,14 +3,14 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <module.h>
-#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
#include <of.h>
+#include <of_address.h>
#include <mfd/syscon.h>
-#include <regmap.h>
-
-#include <dt-bindings/clock/at91.h>
+#include <linux/regmap.h>
#include "pmc.h"
@@ -40,7 +40,7 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname,
}
EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
-struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
+struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
{
unsigned int type = clkspec->args[0];
unsigned int idx = clkspec->args[1];
@@ -63,6 +63,10 @@ struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
if (idx < pmc_data->ngck)
return pmc_data->ghws[idx];
break;
+ case PMC_TYPE_PROGRAMMABLE:
+ if (idx < pmc_data->npck)
+ return pmc_data->pchws[idx];
+ break;
default:
break;
}
@@ -72,213 +76,32 @@ struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
return ERR_PTR(-EINVAL);
}
-void pmc_data_free(struct pmc_data *pmc_data)
-{
- kfree(pmc_data->chws);
- kfree(pmc_data->shws);
- kfree(pmc_data->phws);
- kfree(pmc_data->ghws);
-}
-
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
- unsigned int nperiph, unsigned int ngck)
+ unsigned int nperiph, unsigned int ngck,
+ unsigned int npck)
{
- struct pmc_data *pmc_data = kzalloc(sizeof(*pmc_data), GFP_KERNEL);
+ unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck;
+ struct pmc_data *pmc_data;
+ pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks),
+ GFP_KERNEL);
if (!pmc_data)
return NULL;
pmc_data->ncore = ncore;
- pmc_data->chws = kcalloc(ncore, sizeof(struct clk *), GFP_KERNEL);
- if (!pmc_data->chws)
- goto err;
+ pmc_data->chws = pmc_data->hwtable;
pmc_data->nsystem = nsystem;
- pmc_data->shws = kcalloc(nsystem, sizeof(struct clk *), GFP_KERNEL);
- if (!pmc_data->shws)
- goto err;
+ pmc_data->shws = pmc_data->chws + ncore;
pmc_data->nperiph = nperiph;
- pmc_data->phws = kcalloc(nperiph, sizeof(struct clk *), GFP_KERNEL);
- if (!pmc_data->phws)
- goto err;
+ pmc_data->phws = pmc_data->shws + nsystem;
pmc_data->ngck = ngck;
- pmc_data->ghws = kcalloc(ngck, sizeof(struct clk *), GFP_KERNEL);
- if (!pmc_data->ghws)
- goto err;
-
- return pmc_data;
-
-err:
- pmc_data_free(pmc_data);
-
- return NULL;
-}
-
-#ifdef CONFIG_PM
-static struct regmap *pmcreg;
-
-static u8 registered_ids[PMC_MAX_IDS];
-static u8 registered_pcks[PMC_MAX_PCKS];
-
-static struct
-{
- u32 scsr;
- u32 pcsr0;
- u32 uckr;
- u32 mor;
- u32 mcfr;
- u32 pllar;
- u32 mckr;
- u32 usb;
- u32 imr;
- u32 pcsr1;
- u32 pcr[PMC_MAX_IDS];
- u32 audio_pll0;
- u32 audio_pll1;
- u32 pckr[PMC_MAX_PCKS];
-} pmc_cache;
-
-/*
- * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
- * without alteration in the table, and 0 is for unused clocks.
- */
-void pmc_register_id(u8 id)
-{
- int i;
-
- for (i = 0; i < PMC_MAX_IDS; i++) {
- if (registered_ids[i] == 0) {
- registered_ids[i] = id;
- break;
- }
- if (registered_ids[i] == id)
- break;
- }
-}
-
-/*
- * As Programmable Clock 0 is valid on AT91 chips, there is an offset
- * of 1 between the stored value and the real clock ID.
- */
-void pmc_register_pck(u8 pck)
-{
- int i;
-
- for (i = 0; i < PMC_MAX_PCKS; i++) {
- if (registered_pcks[i] == 0) {
- registered_pcks[i] = pck + 1;
- break;
- }
- if (registered_pcks[i] == (pck + 1))
- break;
- }
-}
-
-static int pmc_suspend(void)
-{
- int i;
- u8 num;
+ pmc_data->ghws = pmc_data->phws + nperiph;
- regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr);
- regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0);
- regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr);
- regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor);
- regmap_read(pmcreg, AT91_CKGR_MCFR, &pmc_cache.mcfr);
- regmap_read(pmcreg, AT91_CKGR_PLLAR, &pmc_cache.pllar);
- regmap_read(pmcreg, AT91_PMC_MCKR, &pmc_cache.mckr);
- regmap_read(pmcreg, AT91_PMC_USB, &pmc_cache.usb);
- regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.imr);
- regmap_read(pmcreg, AT91_PMC_PCSR1, &pmc_cache.pcsr1);
+ pmc_data->npck = npck;
+ pmc_data->pchws = pmc_data->ghws + ngck;
- for (i = 0; registered_ids[i]; i++) {
- regmap_write(pmcreg, AT91_PMC_PCR,
- (registered_ids[i] & AT91_PMC_PCR_PID_MASK));
- regmap_read(pmcreg, AT91_PMC_PCR,
- &pmc_cache.pcr[registered_ids[i]]);
- }
- for (i = 0; registered_pcks[i]; i++) {
- num = registered_pcks[i] - 1;
- regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]);
- }
-
- return 0;
-}
-
-static bool pmc_ready(unsigned int mask)
-{
- unsigned int status;
-
- regmap_read(pmcreg, AT91_PMC_SR, &status);
-
- return ((status & mask) == mask) ? 1 : 0;
-}
-
-static void pmc_resume(void)
-{
- int i;
- u8 num;
- u32 tmp;
- u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
-
- regmap_read(pmcreg, AT91_PMC_MCKR, &tmp);
- if (pmc_cache.mckr != tmp)
- pr_warn("MCKR was not configured properly by the firmware\n");
- regmap_read(pmcreg, AT91_CKGR_PLLAR, &tmp);
- if (pmc_cache.pllar != tmp)
- pr_warn("PLLAR was not configured properly by the firmware\n");
-
- regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
- regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
- regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
- regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
- regmap_write(pmcreg, AT91_CKGR_MCFR, pmc_cache.mcfr);
- regmap_write(pmcreg, AT91_PMC_USB, pmc_cache.usb);
- regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.imr);
- regmap_write(pmcreg, AT91_PMC_PCER1, pmc_cache.pcsr1);
-
- for (i = 0; registered_ids[i]; i++) {
- regmap_write(pmcreg, AT91_PMC_PCR,
- pmc_cache.pcr[registered_ids[i]] |
- AT91_PMC_PCR_CMD);
- }
- for (i = 0; registered_pcks[i]; i++) {
- num = registered_pcks[i] - 1;
- regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
- }
-
- if (pmc_cache.uckr & AT91_PMC_UPLLEN)
- mask |= AT91_PMC_LOCKU;
-
- while (!pmc_ready(mask))
- cpu_relax();
-}
-
-static struct syscore_ops pmc_syscore_ops = {
- .suspend = pmc_suspend,
- .resume = pmc_resume,
-};
-
-static const struct of_device_id sama5d2_pmc_dt_ids[] = {
- { .compatible = "atmel,sama5d2-pmc" },
- { /* sentinel */ }
-};
-
-static int __init pmc_register_ops(void)
-{
- struct device_node *np;
-
- np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
-
- pmcreg = device_node_to_regmap(np);
- if (IS_ERR(pmcreg))
- return PTR_ERR(pmcreg);
-
- register_syscore_ops(&pmc_syscore_ops);
-
- return 0;
+ return pmc_data;
}
-/* This has to happen before arch_initcall because of the tcb_clksrc driver */
-postcore_initcall(pmc_register_ops);
-#endif
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index d96a94e6e5..6c8801a0f9 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -8,19 +8,29 @@
#ifndef __PMC_H_
#define __PMC_H_
-#include <io.h>
-#include <linux/bitops.h>
-#include <printk.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <of.h>
+#include <linux/barebox-wrapper.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/clock/at91.h>
+
+extern spinlock_t pmc_pcr_lock;
struct pmc_data {
unsigned int ncore;
- struct clk **chws;
+ struct clk_hw **chws;
unsigned int nsystem;
- struct clk **shws;
+ struct clk_hw **shws;
unsigned int nperiph;
- struct clk **phws;
+ struct clk_hw **phws;
unsigned int ngck;
- struct clk **ghws;
+ struct clk_hw **ghws;
+ unsigned int npck;
+ struct clk_hw **pchws;
+
+ struct clk_hw *hwtable[];
};
struct clk_range {
@@ -41,14 +51,20 @@ extern const struct clk_master_layout at91sam9x5_master_layout;
struct clk_master_characteristics {
struct clk_range output;
- u32 divisors[4];
+ u32 divisors[5];
u8 have_div3_pres;
};
struct clk_pll_layout {
u32 pllr_mask;
- u16 mul_mask;
+ u32 mul_mask;
+ u32 frac_mask;
+ u32 div_mask;
+ u32 endiv_mask;
u8 mul_shift;
+ u8 frac_shift;
+ u8 div_shift;
+ u8 endiv_shift;
};
extern const struct clk_pll_layout at91rm9200_pll_layout;
@@ -85,134 +101,168 @@ struct clk_pcr_layout {
u32 pid_mask;
};
+/**
+ * struct at91_clk_pms - Power management state for AT91 clock
+ * @rate: clock rate
+ * @parent_rate: clock parent rate
+ * @status: clock status (enabled or disabled)
+ * @parent: clock parent index
+ */
+struct at91_clk_pms {
+ unsigned long rate;
+ unsigned long parent_rate;
+ unsigned int status;
+ unsigned int parent;
+};
+
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
#define ndck(a, s) (a[s - 1].id + 1)
#define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1)
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
- unsigned int nperiph, unsigned int ngck);
-void pmc_data_free(struct pmc_data *pmc_data);
+ unsigned int nperiph, unsigned int ngck,
+ unsigned int npck);
int of_at91_get_clk_range(struct device_node *np, const char *propname,
struct clk_range *range);
-struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data);
+struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_frac(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_pad(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_audio_pll_pmc(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
-at91_clk_register_generated(struct regmap *regmap,
+struct clk_hw * __init
+at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u8 num_parents, u8 id, bool pll_audio,
- const struct clk_range *range);
+ u32 *mux_table, u8 num_parents, u8 id,
+ const struct clk_range *range, int chg_pid);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_h32mx(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
const char * const *parent_names,
unsigned int num_parents, u8 bus_id);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_main_rc_osc(struct regmap *regmap, const char *name,
u32 frequency, u32 accuracy);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_main_osc(struct regmap *regmap, const char *name,
const char *parent_name, bool bypass);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name,
const char **parent_names, int num_parents);
-struct clk * __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);
-
-struct clk * __init
+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);
+
+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,
+ const char *name, int num_parents,
+ const char **parent_names, u32 *mux_table,
+ spinlock_t *lock, u8 id, bool critical,
+ int chg_pid);
+
+struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id);
-struct clk * __init
-at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
+struct clk_hw * __init
+at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
- u32 id, const struct clk_range *range);
+ u32 id, const struct clk_range *range,
+ int chg_pid, unsigned long flags);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_pll(struct regmap *regmap, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_layout *layout,
const struct clk_pll_characteristics *characteristics);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
-sam9x60_clk_register_pll(struct regmap *regmap,
- const char *name, const char *parent_name, u8 id,
- const struct clk_pll_characteristics *characteristics);
+struct clk_hw * __init
+sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, u32 flags);
-struct clk * __init
+struct clk_hw * __init
+sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, u32 flags);
+
+struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents, u8 id,
- const struct clk_programmable_layout *layout);
+ const struct clk_programmable_layout *layout,
+ u32 *mux_table);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_sam9260_slow(struct regmap *regmap,
const char *name,
const char **parent_names,
int num_parents);
-struct clk * __init
+struct clk_hw * __init
at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_system(struct regmap *regmap, const char *name,
- const char *parent_name, u8 id);
+ const char *parent_name, u8 id, unsigned long flags);
-struct clk * __init
+struct clk_hw * __init
at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents);
-struct clk * __init
+struct clk_hw * __init
at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name);
-struct clk * __init
+struct clk_hw * __init
sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents);
-
-struct clk * __init
+struct clk_hw * __init
at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name, const u32 *divisors);
-struct clk * __init
+struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
const char *name, const char *parent_name);
-#ifdef CONFIG_PM
-void pmc_register_id(u8 id);
-void pmc_register_pck(u8 pck);
-#else
-static inline void pmc_register_id(u8 id) {}
-static inline void pmc_register_pck(u8 pck) {}
-#endif
+struct clk_hw * __init
+at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name,
+ const char *parent_name);
#endif /* __PMC_H_ */
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 36a7a846ef..3a477ffc95 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -1,18 +1,16 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#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 },
.divisors = { 1, 2, 4, 3 },
@@ -26,7 +24,7 @@ static const struct clk_master_layout sam9x60_master_layout = {
};
static const struct clk_range plla_outputs[] = {
- { .min = 300000000, .max = 600000000 },
+ { .min = 2343750, .max = 1200000000 },
};
static const struct clk_pll_characteristics plla_characteristics = {
@@ -46,6 +44,20 @@ static const struct clk_pll_characteristics upll_characteristics = {
.upll = true,
};
+static const struct clk_pll_layout pll_frac_layout = {
+ .mul_mask = GENMASK(31, 24),
+ .frac_mask = GENMASK(21, 0),
+ .mul_shift = 24,
+ .frac_shift = 0,
+};
+
+static const struct clk_pll_layout pll_div_layout = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+};
+
static const struct clk_programmable_layout sam9x60_programmable_layout = {
.pres_mask = 0xff,
.pres_shift = 8,
@@ -64,17 +76,23 @@ static const struct clk_pcr_layout sam9x60_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
u8 id;
} sam9x60_systemck[] = {
- { .n = "ddrck", .p = "masterck", .id = 2 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
{ .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 {
char *n;
+ unsigned long flags;
u8 id;
} sam9x60_periphck[] = {
{ .n = "pioA_clk", .id = 2, },
@@ -121,14 +139,17 @@ static const struct {
{ .n = "pioD_clk", .id = 44, },
{ .n = "tcb1_clk", .id = 45, },
{ .n = "dbgu_clk", .id = 47, },
- { .n = "mpddr_clk", .id = 49, },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 49, .flags = CLK_IS_CRITICAL },
};
static const struct {
char *n;
u8 id;
struct clk_range r;
- bool pll;
} sam9x60_gck[] = {
{ .n = "flex0_gclk", .id = 5, },
{ .n = "flex1_gclk", .id = 6, },
@@ -148,11 +169,9 @@ static const struct {
{ .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, },
{ .n = "flex11_gclk", .id = 32, },
{ .n = "flex12_gclk", .id = 33, },
- { .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 },
- .pll = true, },
+ { .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 }, },
{ .n = "pit64b_gclk", .id = 37, },
- { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 },
- .pll = true, },
+ { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, },
{ .n = "tcb1_gclk", .id = 45, },
{ .n = "dbgu_gclk", .id = 47, },
};
@@ -163,10 +182,10 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
const char *td_slck_name, *md_slck_name, *mainxtal_name;
struct pmc_data *sam9x60_pmc;
const char *parent_names[6];
+ struct clk_hw *main_osc_hw;
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
- bool bypass;
i = of_property_match_string(np, "clock-names", "td_slck");
if (i < 0)
@@ -185,28 +204,26 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
- regmap = syscon_node_to_regmap(np);
+ regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
- sam9x60_pmc = pmc_data_allocate(PMC_MAIN + 1,
+ sam9x60_pmc = pmc_data_allocate(PMC_PLLACK + 1,
nck(sam9x60_systemck),
nck(sam9x60_periphck),
- nck(sam9x60_gck));
+ nck(sam9x60_gck), 8);
if (!sam9x60_pmc)
return;
- hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000,
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
50000000);
if (IS_ERR(hw))
goto err_free;
- bypass = of_property_read_bool(np, "atmel,osc-bypass");
-
- hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
- bypass);
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, 0);
if (IS_ERR(hw))
goto err_free;
+ main_osc_hw = hw;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
@@ -216,13 +233,45 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_pmc->chws[PMC_MAIN] = hw;
- hw = sam9x60_clk_register_pll(regmap, "pllack",
- "mainck", 0, &plla_characteristics);
+ hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "pllack_fracck",
+ "mainck", sam9x60_pmc->chws[PMC_MAIN],
+ 0, &plla_characteristics,
+ &pll_frac_layout,
+ /*
+ * This feeds pllack_divck which
+ * feeds CPU. It should not be
+ * disabled.
+ */
+ CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "pllack_divck",
+ "pllack_fracck", 0, &plla_characteristics,
+ &pll_div_layout,
+ /*
+ * This feeds CPU. It should not
+ * be disabled.
+ */
+ CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sam9x60_pmc->chws[PMC_PLLACK] = hw;
+
+ hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "upllck_fracck",
+ "main_osc", main_osc_hw, 1,
+ &upll_characteristics,
+ &pll_frac_layout, CLK_SET_RATE_GATE);
if (IS_ERR(hw))
goto err_free;
- hw = sam9x60_clk_register_pll(regmap, "upllck",
- "main_osc", 1, &upll_characteristics);
+ hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "upllck_divck",
+ "upllck_fracck", 1, &upll_characteristics,
+ &pll_div_layout,
+ CLK_SET_RATE_GATE |
+ CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT);
if (IS_ERR(hw))
goto err_free;
@@ -230,45 +279,55 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[0] = md_slck_name;
parent_names[1] = "mainck";
- parent_names[2] = "pllack";
- hw = at91_clk_register_master(regmap, "masterck", 3, parent_names,
- &sam9x60_master_layout,
- &mck_characteristics);
+ parent_names[2] = "pllack_divck";
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 3,
+ parent_names, &sam9x60_master_layout,
+ &mck_characteristics, &mck_lock);
+ 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;
sam9x60_pmc->chws[PMC_MCK] = hw;
- parent_names[0] = "pllack";
- parent_names[1] = "upllck";
- parent_names[2] = "mainck";
- parent_names[3] = "mainck";
- hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 4);
+ parent_names[0] = "pllack_divck";
+ parent_names[1] = "upllck_divck";
+ parent_names[2] = "main_osc";
+ hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
if (IS_ERR(hw))
goto err_free;
parent_names[0] = md_slck_name;
parent_names[1] = td_slck_name;
parent_names[2] = "mainck";
- parent_names[3] = "masterck";
- parent_names[4] = "pllack";
- parent_names[5] = "upllck";
- for (i = 0; i < 8; i++) {
+ parent_names[3] = "masterck_div";
+ parent_names[4] = "pllack_divck";
+ parent_names[5] = "upllck_divck";
+ for (i = 0; i < 2; i++) {
char name[6];
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
- &sam9x60_programmable_layout);
+ &sam9x60_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
+
+ sam9x60_pmc->pchws[i] = hw;
}
for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) {
hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n,
sam9x60_systemck[i].p,
- sam9x60_systemck[i].id);
+ sam9x60_systemck[i].id,
+ sam9x60_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -276,12 +335,13 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ 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);
+ &range, INT_MIN,
+ sam9x60_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -289,25 +349,24 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) {
- hw = at91_clk_register_generated(regmap,
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sam9x60_pcr_layout,
sam9x60_gck[i].n,
- parent_names, 6,
+ parent_names, NULL, 6,
sam9x60_gck[i].id,
- sam9x60_gck[i].pll,
- &sam9x60_gck[i].r);
+ &sam9x60_gck[i].r, INT_MIN);
if (IS_ERR(hw))
goto err_free;
sam9x60_pmc->ghws[sam9x60_gck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, sam9x60_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x60_pmc);
return;
err_free:
- pmc_data_free(sam9x60_pmc);
+ kfree(sam9x60_pmc);
}
/* Some clks are used for a clocksource */
CLK_OF_DECLARE(sam9x60_pmc, "microchip,sam9x60-pmc", sam9x60_pmc_setup);
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index 731637e4ab..96c0d1f6a4 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -1,18 +1,15 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#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 },
@@ -44,16 +41,21 @@ static const struct clk_pcr_layout sama5d2_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
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 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
+ { .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 {
@@ -95,11 +97,13 @@ static const struct {
{ .n = "i2s1_clk", .id = 55, .r = { .min = 0, .max = 83000000 }, },
{ .n = "can0_clk", .id = 56, .r = { .min = 0, .max = 83000000 }, },
{ .n = "can1_clk", .id = 57, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "ptc_clk", .id = 58, .r = { .min = 0, .max = 83000000 }, },
{ .n = "classd_clk", .id = 59, .r = { .min = 0, .max = 83000000 }, },
};
static const struct {
char *n;
+ unsigned long flags;
u8 id;
} sama5d2_periphck[] = {
{ .n = "dma0_clk", .id = 6, },
@@ -107,7 +111,11 @@ static const struct {
{ .n = "aes_clk", .id = 9, },
{ .n = "aesb_clk", .id = 10, },
{ .n = "sha_clk", .id = 12, },
- { .n = "mpddr_clk", .id = 13, },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 13, .flags = CLK_IS_CRITICAL },
{ .n = "matrix0_clk", .id = 15, },
{ .n = "sdmmc0_hclk", .id = 31, },
{ .n = "sdmmc1_hclk", .id = 32, },
@@ -121,21 +129,30 @@ static const struct {
char *n;
u8 id;
struct clk_range r;
- bool pll;
+ int chg_pid;
} sama5d2_gck[] = {
- { .n = "sdmmc0_gclk", .id = 31, },
- { .n = "sdmmc1_gclk", .id = 32, },
- { .n = "tcb0_gclk", .id = 35, .r = { .min = 0, .max = 83000000 }, },
- { .n = "tcb1_gclk", .id = 36, .r = { .min = 0, .max = 83000000 }, },
- { .n = "pwm_gclk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
- { .n = "isc_gclk", .id = 46, },
- { .n = "pdmic_gclk", .id = 48, },
- { .n = "i2s0_gclk", .id = 54, .pll = true },
- { .n = "i2s1_gclk", .id = 55, .pll = true },
- { .n = "can0_gclk", .id = 56, .r = { .min = 0, .max = 80000000 }, },
- { .n = "can1_gclk", .id = 57, .r = { .min = 0, .max = 80000000 }, },
- { .n = "classd_gclk", .id = 59, .r = { .min = 0, .max = 100000000 },
- .pll = true },
+ { .n = "flx0_gclk", .id = 19, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx1_gclk", .id = 20, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx2_gclk", .id = 21, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx3_gclk", .id = 22, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "flx4_gclk", .id = 23, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart0_gclk", .id = 24, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart1_gclk", .id = 25, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart2_gclk", .id = 26, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart3_gclk", .id = 27, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "uart4_gclk", .id = 28, .chg_pid = INT_MIN, .r = { .min = 0, .max = 27666666 }, },
+ { .n = "sdmmc0_gclk", .id = 31, .chg_pid = INT_MIN, },
+ { .n = "sdmmc1_gclk", .id = 32, .chg_pid = INT_MIN, },
+ { .n = "tcb0_gclk", .id = 35, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "tcb1_gclk", .id = 36, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "pwm_gclk", .id = 38, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "isc_gclk", .id = 46, .chg_pid = INT_MIN, },
+ { .n = "pdmic_gclk", .id = 48, .chg_pid = INT_MIN, },
+ { .n = "i2s0_gclk", .id = 54, .chg_pid = 5, },
+ { .n = "i2s1_gclk", .id = 55, .chg_pid = 5, },
+ { .n = "can0_gclk", .id = 56, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
+ { .n = "can1_gclk", .id = 57, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
+ { .n = "classd_gclk", .id = 59, .chg_pid = 5, .r = { .min = 0, .max = 100000000 }, },
};
static const struct clk_programmable_layout sama5d2_programmable_layout = {
@@ -153,7 +170,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
struct pmc_data *sama5d2_pmc;
const char *parent_names[6];
struct regmap *regmap, *regmap_sfr;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -172,10 +189,10 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- sama5d2_pmc = pmc_data_allocate(PMC_I2S1_MUX + 1,
+ sama5d2_pmc = pmc_data_allocate(PMC_AUDIOPINCK + 1,
nck(sama5d2_systemck),
nck(sama5d2_periph32ck),
- nck(sama5d2_gck));
+ nck(sama5d2_gck), 3);
if (!sama5d2_pmc)
return;
@@ -208,6 +225,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
if (IS_ERR(hw))
goto err_free;
+ sama5d2_pmc->chws[PMC_PLLACK] = hw;
+
hw = at91_clk_register_audio_pll_frac(regmap, "audiopll_fracck",
"mainck");
if (IS_ERR(hw))
@@ -218,11 +237,15 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
if (IS_ERR(hw))
goto err_free;
+ sama5d2_pmc->chws[PMC_AUDIOPINCK] = hw;
+
hw = at91_clk_register_audio_pll_pmc(regmap, "audiopll_pmcck",
"audiopll_fracck");
if (IS_ERR(hw))
goto err_free;
+ sama5d2_pmc->chws[PMC_AUDIOPLLCK] = hw;
+
regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
if (IS_ERR(regmap_sfr))
regmap_sfr = NULL;
@@ -237,15 +260,24 @@ 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);
+ 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;
@@ -261,24 +293,28 @@ 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;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
- &sama5d2_programmable_layout);
+ &sama5d2_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
+
+ sama5d2_pmc->pchws[i] = hw;
}
for (i = 0; i < ARRAY_SIZE(sama5d2_systemck); i++) {
hw = at91_clk_register_system(regmap, sama5d2_systemck[i].n,
sama5d2_systemck[i].p,
- sama5d2_systemck[i].id);
+ sama5d2_systemck[i].id,
+ sama5d2_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -286,12 +322,13 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ 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);
+ &range, INT_MIN,
+ sama5d2_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -299,12 +336,13 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_periph32ck[i].n,
"h32mxck",
sama5d2_periph32ck[i].id,
- &sama5d2_periph32ck[i].r);
+ &sama5d2_periph32ck[i].r,
+ INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
@@ -315,16 +353,16 @@ 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,
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_gck[i].n,
- parent_names, 6,
+ parent_names, NULL, 6,
sama5d2_gck[i].id,
- sama5d2_gck[i].pll,
- &sama5d2_gck[i].r);
+ &sama5d2_gck[i].r,
+ sama5d2_gck[i].chg_pid);
if (IS_ERR(hw))
goto err_free;
@@ -351,11 +389,12 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_pmc->chws[PMC_I2S1_MUX] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d2_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d2_pmc);
return;
err_free:
- pmc_data_free(sama5d2_pmc);
+ kfree(sama5d2_pmc);
}
-CLK_OF_DECLARE_DRIVER(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup);
+
+CLK_OF_DECLARE(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup);
diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c
new file mode 100644
index 0000000000..53a1a7413a
--- /dev/null
+++ b/drivers/clk/at91/sama5d3.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
+#include <mfd/syscon.h>
+#include <linux/slab.h>
+#include <stdio.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#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 },
+};
+
+static u8 plla_out[] = { 0 };
+
+static u16 plla_icpll[] = { 0 };
+
+static const struct clk_range plla_outputs[] = {
+ { .min = 400000000, .max = 1000000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+ .input = { .min = 8000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(plla_outputs),
+ .output = plla_outputs,
+ .icpll = plla_icpll,
+ .out = plla_out,
+};
+
+static const struct clk_pcr_layout sama5d3_pcr_layout = {
+ .offset = 0x10c,
+ .cmd = BIT(12),
+ .pid_mask = GENMASK(6, 0),
+ .div_mask = GENMASK(17, 16),
+};
+
+static const struct {
+ char *n;
+ char *p;
+ unsigned long flags;
+ u8 id;
+} sama5d3_systemck[] = {
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
+ { .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 {
+ char *n;
+ u8 id;
+ struct clk_range r;
+ unsigned long flags;
+} sama5d3_periphck[] = {
+ { .n = "dbgu_clk", .id = 2, },
+ { .n = "hsmc_clk", .id = 5, },
+ { .n = "pioA_clk", .id = 6, },
+ { .n = "pioB_clk", .id = 7, },
+ { .n = "pioC_clk", .id = 8, },
+ { .n = "pioD_clk", .id = 9, },
+ { .n = "pioE_clk", .id = 10, },
+ { .n = "usart0_clk", .id = 12, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "usart1_clk", .id = 13, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "usart2_clk", .id = 14, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "usart3_clk", .id = 15, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "uart0_clk", .id = 16, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "uart1_clk", .id = 17, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "twi0_clk", .id = 18, .r = { .min = 0, .max = 41500000 }, },
+ { .n = "twi1_clk", .id = 19, .r = { .min = 0, .max = 41500000 }, },
+ { .n = "twi2_clk", .id = 20, .r = { .min = 0, .max = 41500000 }, },
+ { .n = "mci0_clk", .id = 21, },
+ { .n = "mci1_clk", .id = 22, },
+ { .n = "mci2_clk", .id = 23, },
+ { .n = "spi0_clk", .id = 24, .r = { .min = 0, .max = 166000000 }, },
+ { .n = "spi1_clk", .id = 25, .r = { .min = 0, .max = 166000000 }, },
+ { .n = "tcb0_clk", .id = 26, .r = { .min = 0, .max = 166000000 }, },
+ { .n = "tcb1_clk", .id = 27, .r = { .min = 0, .max = 166000000 }, },
+ { .n = "pwm_clk", .id = 28, },
+ { .n = "adc_clk", .id = 29, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "dma0_clk", .id = 30, },
+ { .n = "dma1_clk", .id = 31, },
+ { .n = "uhphs_clk", .id = 32, },
+ { .n = "udphs_clk", .id = 33, },
+ { .n = "macb0_clk", .id = 34, },
+ { .n = "macb1_clk", .id = 35, },
+ { .n = "lcdc_clk", .id = 36, },
+ { .n = "isi_clk", .id = 37, },
+ { .n = "ssc0_clk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "ssc1_clk", .id = 39, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "can0_clk", .id = 40, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "can1_clk", .id = 41, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "sha_clk", .id = 42, },
+ { .n = "aes_clk", .id = 43, },
+ { .n = "tdes_clk", .id = 44, },
+ { .n = "trng_clk", .id = 45, },
+ { .n = "fuse_clk", .id = 48, },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 49, .flags = CLK_IS_CRITICAL },
+};
+
+static void __init sama5d3_pmc_setup(struct device_node *np)
+{
+ const char *slck_name, *mainxtal_name;
+ struct pmc_data *sama5d3_pmc;
+ const char *parent_names[5];
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ int i;
+ bool bypass;
+
+ i = of_property_match_string(np, "clock-names", "slow_clk");
+ if (i < 0)
+ return;
+
+ slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = device_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ sama5d3_pmc = pmc_data_allocate(PMC_PLLACK + 1,
+ nck(sama5d3_systemck),
+ nck(sama5d3_periphck), 0, 3);
+ if (!sama5d3_pmc)
+ return;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ &sama5d3_pll_layout, &plla_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d3_pmc->chws[PMC_PLLACK] = hw;
+
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d3_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
+ parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics, &mck_lock);
+ 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;
+
+ sama5d3_pmc->chws[PMC_MCK] = hw;
+
+ parent_names[0] = "plladivck";
+ parent_names[1] = "utmick";
+ hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91sam9x5_clk_register_smd(regmap, "smdclk", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ parent_names[4] = "masterck_div";
+ for (i = 0; i < 3; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 5, i,
+ &at91sam9x5_programmable_layout,
+ NULL);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d3_pmc->pchws[i] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama5d3_systemck); i++) {
+ hw = at91_clk_register_system(regmap, sama5d3_systemck[i].n,
+ sama5d3_systemck[i].p,
+ sama5d3_systemck[i].id,
+ sama5d3_systemck[i].flags);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d3_pmc->shws[sama5d3_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama5d3_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ &sama5d3_pcr_layout,
+ sama5d3_periphck[i].n,
+ "masterck_div",
+ sama5d3_periphck[i].id,
+ &sama5d3_periphck[i].r,
+ INT_MIN,
+ sama5d3_periphck[i].flags);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d3_pmc->phws[sama5d3_periphck[i].id] = hw;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d3_pmc);
+
+ return;
+
+err_free:
+ kfree(sama5d3_pmc);
+}
+/*
+ * The TCB is used as the clocksource so its clock is needed early. This means
+ * this can't be a platform driver.
+ */
+CLK_OF_DECLARE(sama5d3_pmc, "atmel,sama5d3-pmc", sama5d3_pmc_setup);
diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c
index 77ccd77404..8fbd810883 100644
--- a/drivers/clk/at91/sama5d4.c
+++ b/drivers/clk/at91/sama5d4.c
@@ -1,18 +1,15 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <driver.h>
-#include <regmap.h>
-#include <stdio.h>
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/clk-provider.h>
#include <mfd/syscon.h>
-
-#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/types.h>
+#include <stdio.h>
#include <dt-bindings/clock/at91.h>
#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 },
@@ -43,16 +40,21 @@ static const struct clk_pcr_layout sama5d4_pcr_layout = {
static const struct {
char *n;
char *p;
+ unsigned long flags;
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 },
+ /*
+ * ddrck feeds DDR controller and is enabled by bootloader thus we need
+ * to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL },
+ { .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 {
@@ -107,12 +109,17 @@ static const struct {
static const struct {
char *n;
+ unsigned long flags;
u8 id;
} sama5d4_periphck[] = {
{ .n = "dma0_clk", .id = 8 },
{ .n = "cpkcc_clk", .id = 10 },
{ .n = "aesb_clk", .id = 13 },
- { .n = "mpddr_clk", .id = 16 },
+ /*
+ * mpddr_clk feeds DDR controller and is enabled by bootloader thus we
+ * need to keep it enabled in case there is no Linux consumer for it.
+ */
+ { .n = "mpddr_clk", .id = 16, .flags = CLK_IS_CRITICAL },
{ .n = "matrix0_clk", .id = 18 },
{ .n = "vdec_clk", .id = 19 },
{ .n = "dma1_clk", .id = 50 },
@@ -127,7 +134,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
struct pmc_data *sama5d4_pmc;
const char *parent_names[5];
struct regmap *regmap;
- struct clk *hw;
+ struct clk_hw *hw;
int i;
bool bypass;
@@ -146,9 +153,9 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- sama5d4_pmc = pmc_data_allocate(PMC_MCK2 + 1,
+ sama5d4_pmc = pmc_data_allocate(PMC_PLLACK + 1,
nck(sama5d4_systemck),
- nck(sama5d4_periph32ck), 0);
+ nck(sama5d4_periph32ck), 0, 3);
if (!sama5d4_pmc)
return;
@@ -179,6 +186,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
if (IS_ERR(hw))
goto err_free;
+ sama5d4_pmc->chws[PMC_PLLACK] = hw;
+
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
if (IS_ERR(hw))
goto err_free;
@@ -189,15 +198,24 @@ 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);
+ 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;
@@ -219,23 +237,27 @@ 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;
+ char name[6];
- name = xasprintf("prog%d", i);
+ snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;
+
+ sama5d4_pmc->pchws[i] = hw;
}
for (i = 0; i < ARRAY_SIZE(sama5d4_systemck); i++) {
hw = at91_clk_register_system(regmap, sama5d4_systemck[i].n,
sama5d4_systemck[i].p,
- sama5d4_systemck[i].id);
+ sama5d4_systemck[i].id,
+ sama5d4_systemck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -243,12 +265,13 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d4_periphck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ 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);
+ &range, INT_MIN,
+ sama5d4_periphck[i].flags);
if (IS_ERR(hw))
goto err_free;
@@ -256,23 +279,24 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
}
for (i = 0; i < ARRAY_SIZE(sama5d4_periph32ck); i++) {
- hw = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama5d4_pcr_layout,
sama5d4_periph32ck[i].n,
"h32mxck",
sama5d4_periph32ck[i].id,
- &range);
+ &range, INT_MIN, 0);
if (IS_ERR(hw))
goto err_free;
sama5d4_pmc->phws[sama5d4_periph32ck[i].id] = hw;
}
- of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d4_pmc);
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama5d4_pmc);
return;
err_free:
- pmc_data_free(sama5d4_pmc);
+ kfree(sama5d4_pmc);
}
-CLK_OF_DECLARE_DRIVER(sama5d4_pmc, "atmel,sama5d4-pmc", sama5d4_pmc_setup);
+
+CLK_OF_DECLARE(sama5d4_pmc, "atmel,sama5d4-pmc", sama5d4_pmc_setup);
diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
new file mode 100644
index 0000000000..7d33367176
--- /dev/null
+++ b/drivers/clk/at91/sama7g5.c
@@ -0,0 +1,1133 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SAMA7G5 PMC code.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <mfd/syscon.h>
+#include <linux/slab.h>
+#include <stdio.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+#define SAMA7G5_INIT_TABLE(_table, _count) \
+ do { \
+ u8 _i; \
+ for (_i = 0; _i < (_count); _i++) \
+ (_table)[_i] = _i; \
+ } while (0)
+
+#define SAMA7G5_FILL_TABLE(_to, _from, _count) \
+ do { \
+ u8 _i; \
+ for (_i = 0; _i < (_count); _i++) { \
+ (_to)[_i] = (_from)[_i]; \
+ } \
+ } while (0)
+
+static DEFINE_SPINLOCK(pmc_pll_lock);
+static DEFINE_SPINLOCK(pmc_mck0_lock);
+static DEFINE_SPINLOCK(pmc_mckX_lock);
+
+/*
+ * PLL clocks identifiers
+ * @PLL_ID_CPU: CPU PLL identifier
+ * @PLL_ID_SYS: System PLL identifier
+ * @PLL_ID_DDR: DDR PLL identifier
+ * @PLL_ID_IMG: Image subsystem PLL identifier
+ * @PLL_ID_BAUD: Baud PLL identifier
+ * @PLL_ID_AUDIO: Audio PLL identifier
+ * @PLL_ID_ETH: Ethernet PLL identifier
+ */
+enum pll_ids {
+ PLL_ID_CPU,
+ PLL_ID_SYS,
+ PLL_ID_DDR,
+ PLL_ID_IMG,
+ PLL_ID_BAUD,
+ PLL_ID_AUDIO,
+ PLL_ID_ETH,
+ PLL_ID_MAX,
+};
+
+/*
+ * PLL type identifiers
+ * @PLL_TYPE_FRAC: fractional PLL identifier
+ * @PLL_TYPE_DIV: divider PLL identifier
+ */
+enum pll_type {
+ PLL_TYPE_FRAC,
+ PLL_TYPE_DIV,
+};
+
+/* Layout for fractional PLLs. */
+static const struct clk_pll_layout pll_layout_frac = {
+ .mul_mask = GENMASK(31, 24),
+ .frac_mask = GENMASK(21, 0),
+ .mul_shift = 24,
+ .frac_shift = 0,
+};
+
+/* Layout for DIVPMC dividers. */
+static const struct clk_pll_layout pll_layout_divpmc = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+};
+
+/* Layout for DIVIO dividers. */
+static const struct clk_pll_layout pll_layout_divio = {
+ .div_mask = GENMASK(19, 12),
+ .endiv_mask = BIT(30),
+ .div_shift = 12,
+ .endiv_shift = 30,
+};
+
+/*
+ * CPU PLL output range.
+ * Notice: The upper limit has been setup to 1000000002 due to hardware
+ * block which cannot output exactly 1GHz.
+ */
+static const struct clk_range cpu_pll_outputs[] = {
+ { .min = 2343750, .max = 1000000002 },
+};
+
+/* PLL output range. */
+static const struct clk_range pll_outputs[] = {
+ { .min = 2343750, .max = 1200000000 },
+};
+
+/* CPU PLL characteristics. */
+static const struct clk_pll_characteristics cpu_pll_characteristics = {
+ .input = { .min = 12000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(cpu_pll_outputs),
+ .output = cpu_pll_outputs,
+};
+
+/* PLL characteristics. */
+static const struct clk_pll_characteristics pll_characteristics = {
+ .input = { .min = 12000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(pll_outputs),
+ .output = pll_outputs,
+};
+
+/*
+ * PLL clocks description
+ * @n: clock name
+ * @p: clock parent
+ * @l: clock layout
+ * @c: clock characteristics
+ * @t: clock type
+ * @f: clock flags
+ * @eid: export index in sama7g5->chws[] array
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ const struct clk_pll_layout *l;
+ const struct clk_pll_characteristics *c;
+ unsigned long f;
+ u8 t;
+ u8 eid;
+} sama7g5_plls[][PLL_ID_MAX] = {
+ [PLL_ID_CPU] = {
+ { .n = "cpupll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &cpu_pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds cpupll_divpmcck which feeds CPU. It should
+ * not be disabled.
+ */
+ .f = CLK_IS_CRITICAL, },
+
+ { .n = "cpupll_divpmcck",
+ .p = "cpupll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &cpu_pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /* This feeds CPU. It should not be disabled. */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
+ .eid = PMC_CPUPLL, },
+ },
+
+ [PLL_ID_SYS] = {
+ { .n = "syspll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds syspll_divpmcck which may feed critical parts
+ * of the systems like timers. Therefore it should not be
+ * disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
+
+ { .n = "syspll_divpmcck",
+ .p = "syspll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /*
+ * This may feed critical parts of the systems like timers.
+ * Therefore it should not be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
+ .eid = PMC_SYSPLL, },
+ },
+
+ [PLL_ID_DDR] = {
+ { .n = "ddrpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ /*
+ * This feeds ddrpll_divpmcck which feeds DDR. It should not
+ * be disabled.
+ */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
+
+ { .n = "ddrpll_divpmcck",
+ .p = "ddrpll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ /* This feeds DDR. It should not be disabled. */
+ .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, },
+ },
+
+ [PLL_ID_IMG] = {
+ { .n = "imgpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ { .n = "imgpll_divpmcck",
+ .p = "imgpll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT, },
+ },
+
+ [PLL_ID_BAUD] = {
+ { .n = "baudpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ { .n = "baudpll_divpmcck",
+ .p = "baudpll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT, },
+ },
+
+ [PLL_ID_AUDIO] = {
+ { .n = "audiopll_fracck",
+ .p = "main_xtal",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ { .n = "audiopll_divpmcck",
+ .p = "audiopll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_AUDIOPMCPLL, },
+
+ { .n = "audiopll_diviock",
+ .p = "audiopll_fracck",
+ .l = &pll_layout_divio,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT,
+ .eid = PMC_AUDIOIOPLL, },
+ },
+
+ [PLL_ID_ETH] = {
+ { .n = "ethpll_fracck",
+ .p = "main_xtal",
+ .l = &pll_layout_frac,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_FRAC,
+ .f = CLK_SET_RATE_GATE, },
+
+ { .n = "ethpll_divpmcck",
+ .p = "ethpll_fracck",
+ .l = &pll_layout_divpmc,
+ .c = &pll_characteristics,
+ .t = PLL_TYPE_DIV,
+ .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT, },
+ },
+};
+
+/*
+ * Master clock (MCK[1..4]) description
+ * @n: clock name
+ * @ep: extra parents names array
+ * @ep_chg_chg_id: index in parents array that specifies the changeable
+ * parent
+ * @ep_count: extra parents count
+ * @ep_mux_table: mux table for extra parents
+ * @id: clock id
+ * @eid: export index in sama7g5->chws[] array
+ * @c: true if clock is critical and cannot be disabled
+ */
+static const struct {
+ const char *n;
+ const char *ep[4];
+ int ep_chg_id;
+ u8 ep_count;
+ u8 ep_mux_table[4];
+ u8 id;
+ u8 eid;
+ u8 c;
+} sama7g5_mckx[] = {
+ { .n = "mck1",
+ .id = 1,
+ .ep = { "syspll_divpmcck", },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .eid = PMC_MCK1,
+ .c = 1, },
+
+ { .n = "mck2",
+ .id = 2,
+ .ep = { "ddrpll_divpmcck", },
+ .ep_mux_table = { 6, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+
+ { .n = "mck3",
+ .id = 3,
+ .ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", },
+ .ep_mux_table = { 5, 6, 7, },
+ .ep_count = 3,
+ .ep_chg_id = 5, },
+
+ { .n = "mck4",
+ .id = 4,
+ .ep = { "syspll_divpmcck", },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+};
+
+/*
+ * System clock description
+ * @n: clock name
+ * @p: clock parent name
+ * @id: clock id
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ u8 id;
+} sama7g5_systemck[] = {
+ { .n = "pck0", .p = "prog0", .id = 8, },
+ { .n = "pck1", .p = "prog1", .id = 9, },
+ { .n = "pck2", .p = "prog2", .id = 10, },
+ { .n = "pck3", .p = "prog3", .id = 11, },
+ { .n = "pck4", .p = "prog4", .id = 12, },
+ { .n = "pck5", .p = "prog5", .id = 13, },
+ { .n = "pck6", .p = "prog6", .id = 14, },
+ { .n = "pck7", .p = "prog7", .id = 15, },
+};
+
+/* Mux table for programmable clocks. */
+static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 5, 6, 7, 8, 9, 10, };
+
+/*
+ * Peripheral clock description
+ * @n: clock name
+ * @p: clock parent name
+ * @r: clock range values
+ * @id: clock id
+ * @chgp: index in parent array of the changeable parent
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ struct clk_range r;
+ u8 chgp;
+ u8 id;
+} sama7g5_periphck[] = {
+ { .n = "pioA_clk", .p = "mck0", .id = 11, },
+ { .n = "securam_clk", .p = "mck0", .id = 18, },
+ { .n = "sfr_clk", .p = "mck1", .id = 19, },
+ { .n = "hsmc_clk", .p = "mck1", .id = 21, },
+ { .n = "xdmac0_clk", .p = "mck1", .id = 22, },
+ { .n = "xdmac1_clk", .p = "mck1", .id = 23, },
+ { .n = "xdmac2_clk", .p = "mck1", .id = 24, },
+ { .n = "acc_clk", .p = "mck1", .id = 25, },
+ { .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 = "csi_clk", .p = "mck3", .id = 33, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "csi2dc_clk", .p = "mck3", .id = 34, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "eic_clk", .p = "mck1", .id = 37, },
+ { .n = "flex0_clk", .p = "mck1", .id = 38, },
+ { .n = "flex1_clk", .p = "mck1", .id = 39, },
+ { .n = "flex2_clk", .p = "mck1", .id = 40, },
+ { .n = "flex3_clk", .p = "mck1", .id = 41, },
+ { .n = "flex4_clk", .p = "mck1", .id = 42, },
+ { .n = "flex5_clk", .p = "mck1", .id = 43, },
+ { .n = "flex6_clk", .p = "mck1", .id = 44, },
+ { .n = "flex7_clk", .p = "mck1", .id = 45, },
+ { .n = "flex8_clk", .p = "mck1", .id = 46, },
+ { .n = "flex9_clk", .p = "mck1", .id = 47, },
+ { .n = "flex10_clk", .p = "mck1", .id = 48, },
+ { .n = "flex11_clk", .p = "mck1", .id = 49, },
+ { .n = "gmac0_clk", .p = "mck1", .id = 51, },
+ { .n = "gmac1_clk", .p = "mck1", .id = 52, },
+ { .n = "icm_clk", .p = "mck1", .id = 55, },
+ { .n = "isc_clk", .p = "mck3", .id = 56, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "i2smcc0_clk", .p = "mck1", .id = 57, .r = { .max = 200000000, }, },
+ { .n = "i2smcc1_clk", .p = "mck1", .id = 58, .r = { .max = 200000000, }, },
+ { .n = "matrix_clk", .p = "mck1", .id = 60, },
+ { .n = "mcan0_clk", .p = "mck1", .id = 61, .r = { .max = 200000000, }, },
+ { .n = "mcan1_clk", .p = "mck1", .id = 62, .r = { .max = 200000000, }, },
+ { .n = "mcan2_clk", .p = "mck1", .id = 63, .r = { .max = 200000000, }, },
+ { .n = "mcan3_clk", .p = "mck1", .id = 64, .r = { .max = 200000000, }, },
+ { .n = "mcan4_clk", .p = "mck1", .id = 65, .r = { .max = 200000000, }, },
+ { .n = "mcan5_clk", .p = "mck1", .id = 66, .r = { .max = 200000000, }, },
+ { .n = "pdmc0_clk", .p = "mck1", .id = 68, .r = { .max = 200000000, }, },
+ { .n = "pdmc1_clk", .p = "mck1", .id = 69, .r = { .max = 200000000, }, },
+ { .n = "pit64b0_clk", .p = "mck1", .id = 70, },
+ { .n = "pit64b1_clk", .p = "mck1", .id = 71, },
+ { .n = "pit64b2_clk", .p = "mck1", .id = 72, },
+ { .n = "pit64b3_clk", .p = "mck1", .id = 73, },
+ { .n = "pit64b4_clk", .p = "mck1", .id = 74, },
+ { .n = "pit64b5_clk", .p = "mck1", .id = 75, },
+ { .n = "pwm_clk", .p = "mck1", .id = 77, },
+ { .n = "qspi0_clk", .p = "mck1", .id = 78, },
+ { .n = "qspi1_clk", .p = "mck1", .id = 79, },
+ { .n = "sdmmc0_clk", .p = "mck1", .id = 80, },
+ { .n = "sdmmc1_clk", .p = "mck1", .id = 81, },
+ { .n = "sdmmc2_clk", .p = "mck1", .id = 82, },
+ { .n = "sha_clk", .p = "mck1", .id = 83, },
+ { .n = "spdifrx_clk", .p = "mck1", .id = 84, .r = { .max = 200000000, }, },
+ { .n = "spdiftx_clk", .p = "mck1", .id = 85, .r = { .max = 200000000, }, },
+ { .n = "ssc0_clk", .p = "mck1", .id = 86, .r = { .max = 200000000, }, },
+ { .n = "ssc1_clk", .p = "mck1", .id = 87, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch0_clk", .p = "mck1", .id = 88, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch1_clk", .p = "mck1", .id = 89, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch2_clk", .p = "mck1", .id = 90, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch0_clk", .p = "mck1", .id = 91, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch1_clk", .p = "mck1", .id = 92, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch2_clk", .p = "mck1", .id = 93, .r = { .max = 200000000, }, },
+ { .n = "tcpca_clk", .p = "mck1", .id = 94, },
+ { .n = "tcpcb_clk", .p = "mck1", .id = 95, },
+ { .n = "tdes_clk", .p = "mck1", .id = 96, },
+ { .n = "trng_clk", .p = "mck1", .id = 97, },
+ { .n = "udphsa_clk", .p = "mck1", .id = 104, },
+ { .n = "udphsb_clk", .p = "mck1", .id = 105, },
+ { .n = "uhphs_clk", .p = "mck1", .id = 106, },
+};
+
+/*
+ * Generic clock description
+ * @n: clock name
+ * @pp: PLL parents
+ * @pp_mux_table: PLL parents mux table
+ * @r: clock output range
+ * @pp_chg_id: id in parent array of changeable PLL parent
+ * @pp_count: PLL parents count
+ * @id: clock id
+ */
+static const struct {
+ const char *n;
+ const char *pp[8];
+ const char pp_mux_table[8];
+ struct clk_range r;
+ int pp_chg_id;
+ u8 pp_count;
+ u8 id;
+} sama7g5_gck[] = {
+ { .n = "adc_gclk",
+ .id = 26,
+ .r = { .max = 100000000, },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 7, 9, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "asrc_gclk",
+ .id = 30,
+ .r = { .max = 200000000 },
+ .pp = { "audiopll_divpmcck", },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = 3, },
+
+ { .n = "csi_gclk",
+ .id = 33,
+ .r = { .max = 27000000 },
+ .pp = { "ddrpll_divpmcck", "imgpll_divpmcck", },
+ .pp_mux_table = { 6, 7, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex0_gclk",
+ .id = 38,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex1_gclk",
+ .id = 39,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex2_gclk",
+ .id = 40,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex3_gclk",
+ .id = 41,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex4_gclk",
+ .id = 42,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex5_gclk",
+ .id = 43,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex6_gclk",
+ .id = 44,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex7_gclk",
+ .id = 45,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex8_gclk",
+ .id = 46,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex9_gclk",
+ .id = 47,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex10_gclk",
+ .id = 48,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex11_gclk",
+ .id = 49,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac0_gclk",
+ .id = 51,
+ .r = { .max = 125000000 },
+ .pp = { "ethpll_divpmcck", },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = 3, },
+
+ { .n = "gmac1_gclk",
+ .id = 52,
+ .r = { .max = 50000000 },
+ .pp = { "ethpll_divpmcck", },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac0_tsu_gclk",
+ .id = 53,
+ .r = { .max = 300000000 },
+ .pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 9, 10, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac1_tsu_gclk",
+ .id = 54,
+ .r = { .max = 300000000 },
+ .pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 9, 10, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "i2smcc0_gclk",
+ .id = 57,
+ .r = { .max = 100000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "i2smcc1_gclk",
+ .id = 58,
+ .r = { .max = 100000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "mcan0_gclk",
+ .id = 61,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan1_gclk",
+ .id = 62,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan2_gclk",
+ .id = 63,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan3_gclk",
+ .id = 64,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan4_gclk",
+ .id = 65,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan5_gclk",
+ .id = 66,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pdmc0_gclk",
+ .id = 68,
+ .r = { .max = 50000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pdmc1_gclk",
+ .id = 69,
+ .r = { .max = 50000000, },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b0_gclk",
+ .id = 70,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b1_gclk",
+ .id = 71,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b2_gclk",
+ .id = 72,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b3_gclk",
+ .id = 73,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b4_gclk",
+ .id = 74,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b5_gclk",
+ .id = 75,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "qspi0_gclk",
+ .id = 78,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "qspi1_gclk",
+ .id = 79,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "sdmmc0_gclk",
+ .id = 80,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "sdmmc1_gclk",
+ .id = 81,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "sdmmc2_gclk",
+ .id = 82,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "spdifrx_gclk",
+ .id = 84,
+ .r = { .max = 150000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "spdiftx_gclk",
+ .id = 85,
+ .r = { .max = 25000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 4, },
+
+ { .n = "tcb0_ch0_gclk",
+ .id = 88,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcb1_ch0_gclk",
+ .id = 91,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcpca_gclk",
+ .id = 94,
+ .r = { .max = 32768, },
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcpcb_gclk",
+ .id = 95,
+ .r = { .max = 32768, },
+ .pp_chg_id = INT_MIN, },
+};
+
+/* MCK0 characteristics. */
+static const struct clk_master_characteristics mck0_characteristics = {
+ .output = { .min = 32768, .max = 200000000 },
+ .divisors = { 1, 2, 4, 3, 5 },
+ .have_div3_pres = 1,
+};
+
+/* MCK0 layout. */
+static const struct clk_master_layout mck0_layout = {
+ .mask = 0x773,
+ .pres_shift = 4,
+ .offset = 0x28,
+};
+
+/* Programmable clock layout. */
+static const struct clk_programmable_layout programmable_layout = {
+ .pres_mask = 0xff,
+ .pres_shift = 8,
+ .css_mask = 0x1f,
+ .have_slck_mck = 0,
+ .is_pres_direct = 1,
+};
+
+/* Peripheral clock layout. */
+static const struct clk_pcr_layout sama7g5_pcr_layout = {
+ .offset = 0x88,
+ .cmd = BIT(31),
+ .gckcss_mask = GENMASK(12, 8),
+ .pid_mask = GENMASK(6, 0),
+};
+
+static void __init sama7g5_pmc_setup(struct device_node *np)
+{
+ const char *td_slck_name, *md_slck_name, *mainxtal_name;
+ struct pmc_data *sama7g5_pmc;
+ const char *parent_names[10];
+ void **alloc_mem = NULL;
+ int alloc_mem_size = 0;
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ bool bypass;
+ int i, j;
+
+ i = of_property_match_string(np, "clock-names", "td_slck");
+ if (i < 0)
+ return;
+
+ td_slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "md_slck");
+ if (i < 0)
+ return;
+
+ md_slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = device_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ sama7g5_pmc = pmc_data_allocate(PMC_MCK1 + 1,
+ nck(sama7g5_systemck),
+ nck(sama7g5_periphck),
+ nck(sama7g5_gck), 8);
+ if (!sama7g5_pmc)
+ return;
+
+ alloc_mem = kmalloc(sizeof(void *) *
+ (ARRAY_SIZE(sama7g5_mckx) + ARRAY_SIZE(sama7g5_gck)),
+ GFP_KERNEL);
+ if (!alloc_mem)
+ goto err_free;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->chws[PMC_MAIN] = hw;
+
+ for (i = 0; i < PLL_ID_MAX; i++) {
+ for (j = 0; j < 3; j++) {
+ struct clk_hw *parent_hw;
+
+ if (!sama7g5_plls[i][j].n)
+ continue;
+
+ switch (sama7g5_plls[i][j].t) {
+ case PLL_TYPE_FRAC:
+ if (!strcmp(sama7g5_plls[i][j].p, "mainck"))
+ parent_hw = sama7g5_pmc->chws[PMC_MAIN];
+ else
+ parent_hw = __clk_get_hw(of_clk_get_by_name(np,
+ sama7g5_plls[i][j].p));
+
+ hw = sam9x60_clk_register_frac_pll(regmap,
+ &pmc_pll_lock, sama7g5_plls[i][j].n,
+ sama7g5_plls[i][j].p, parent_hw, i,
+ sama7g5_plls[i][j].c,
+ sama7g5_plls[i][j].l,
+ sama7g5_plls[i][j].f);
+ break;
+
+ case PLL_TYPE_DIV:
+ hw = sam9x60_clk_register_div_pll(regmap,
+ &pmc_pll_lock, sama7g5_plls[i][j].n,
+ sama7g5_plls[i][j].p, i,
+ sama7g5_plls[i][j].c,
+ sama7g5_plls[i][j].l,
+ sama7g5_plls[i][j].f);
+ break;
+
+ default:
+ continue;
+ }
+
+ if (IS_ERR(hw))
+ goto err_free;
+
+ if (sama7g5_plls[i][j].eid)
+ sama7g5_pmc->chws[sama7g5_plls[i][j].eid] = hw;
+ }
+ }
+
+ parent_names[0] = "cpupll_divpmcck";
+ hw = at91_clk_register_master_div(regmap, "mck0", "cpupll_divpmcck",
+ &mck0_layout, &mck0_characteristics,
+ &pmc_mck0_lock, CLK_GET_RATE_NOCACHE);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->chws[PMC_MCK] = hw;
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
+ u8 num_parents = 3 + sama7g5_mckx[i].ep_count;
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ SAMA7G5_INIT_TABLE(mux_table, 3);
+ SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_mckx[i].ep_mux_table,
+ sama7g5_mckx[i].ep_count);
+ SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_mckx[i].ep,
+ sama7g5_mckx[i].ep_count);
+
+ hw = at91_clk_sama7g5_register_master(regmap, sama7g5_mckx[i].n,
+ num_parents, parent_names, mux_table,
+ &pmc_mckX_lock, sama7g5_mckx[i].id,
+ sama7g5_mckx[i].c,
+ sama7g5_mckx[i].ep_chg_id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ alloc_mem[alloc_mem_size++] = mux_table;
+
+ if (sama7g5_mckx[i].eid)
+ sama7g5_pmc->chws[sama7g5_mckx[i].eid] = hw;
+ }
+
+ hw = at91_clk_sama7g5_register_utmi(regmap, "utmick", "main_xtal");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ parent_names[3] = "syspll_divpmcck";
+ parent_names[4] = "ddrpll_divpmcck";
+ parent_names[5] = "imgpll_divpmcck";
+ parent_names[6] = "baudpll_divpmcck";
+ parent_names[7] = "audiopll_divpmcck";
+ parent_names[8] = "ethpll_divpmcck";
+ for (i = 0; i < 8; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name, parent_names,
+ 9, i,
+ &programmable_layout,
+ sama7g5_prog_mux_table);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->pchws[i] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) {
+ hw = at91_clk_register_system(regmap, sama7g5_systemck[i].n,
+ sama7g5_systemck[i].p,
+ sama7g5_systemck[i].id, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->shws[sama7g5_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ &sama7g5_pcr_layout,
+ sama7g5_periphck[i].n,
+ sama7g5_periphck[i].p,
+ sama7g5_periphck[i].id,
+ &sama7g5_periphck[i].r,
+ sama7g5_periphck[i].chgp ? 0 :
+ INT_MIN, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->phws[sama7g5_periphck[i].id] = hw;
+ }
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
+ u8 num_parents = 3 + sama7g5_gck[i].pp_count;
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ SAMA7G5_INIT_TABLE(mux_table, 3);
+ SAMA7G5_FILL_TABLE(&mux_table[3], sama7g5_gck[i].pp_mux_table,
+ sama7g5_gck[i].pp_count);
+ SAMA7G5_FILL_TABLE(&parent_names[3], sama7g5_gck[i].pp,
+ sama7g5_gck[i].pp_count);
+
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
+ &sama7g5_pcr_layout,
+ sama7g5_gck[i].n,
+ parent_names, mux_table,
+ num_parents,
+ sama7g5_gck[i].id,
+ &sama7g5_gck[i].r,
+ sama7g5_gck[i].pp_chg_id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->ghws[sama7g5_gck[i].id] = hw;
+ alloc_mem[alloc_mem_size++] = mux_table;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama7g5_pmc);
+
+ return;
+
+err_free:
+ if (alloc_mem) {
+ for (i = 0; i < alloc_mem_size; i++)
+ kfree(alloc_mem[i]);
+ kfree(alloc_mem);
+ }
+
+ kfree(sama7g5_pmc);
+}
+
+/* Some clks are used for a clocksource */
+CLK_OF_DECLARE(sama7g5_pmc, "microchip,sama7g5-pmc", sama7g5_pmc_setup);
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index 1a33a64421..1e03537cf1 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -5,18 +5,12 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
-#include <common.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
#include <clock.h>
#include <of.h>
#include <of_address.h>
-#include <io.h>
-#include <linux/list.h>
-#include <linux/clk.h>
-#include <linux/clk/at91_pmc.h>
-#include <mfd/syscon.h>
-#include <regmap.h>
-
-
+#include <linux/io.h>
#define SLOW_CLOCK_FREQ 32768
#define SLOWCK_SW_CYCLES 5
@@ -33,50 +27,47 @@ struct clk_slow_bits {
};
struct clk_slow_osc {
- struct clk clk;
+ struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
unsigned long startup_usec;
- const char *parent_name;
};
-#define to_clk_slow_osc(clk) container_of(clk, struct clk_slow_osc, clk)
+#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
struct clk_sama5d4_slow_osc {
- struct clk clk;
+ struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
unsigned long startup_usec;
bool prepared;
- const char *parent_name;
};
-#define to_clk_sama5d4_slow_osc(clk) container_of(clk, struct clk_sama5d4_slow_osc, clk)
+#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)
struct clk_slow_rc_osc {
- struct clk clk;
+ struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
unsigned long frequency;
+ unsigned long accuracy;
unsigned long startup_usec;
- const char *parent_name;
};
-#define to_clk_slow_rc_osc(clk) container_of(clk, struct clk_slow_rc_osc, clk)
+#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
struct clk_sam9x5_slow {
- struct clk clk;
+ struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
u8 parent;
- const char *parent_names[2];
};
-#define to_clk_sam9x5_slow(clk) container_of(clk, struct clk_sam9x5_slow, clk)
+#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
-static int clk_slow_osc_enable(struct clk *clk)
+static int clk_slow_osc_prepare(struct clk_hw *hw)
{
- struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+ struct clk_slow_osc *osc = to_clk_slow_osc(hw);
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
@@ -90,9 +81,9 @@ static int clk_slow_osc_enable(struct clk *clk)
return 0;
}
-static void clk_slow_osc_disable(struct clk *clk)
+static void clk_slow_osc_unprepare(struct clk_hw *hw)
{
- struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+ struct clk_slow_osc *osc = to_clk_slow_osc(hw);
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
@@ -102,9 +93,9 @@ static void clk_slow_osc_disable(struct clk *clk)
writel(tmp & ~osc->bits->cr_osc32en, sckcr);
}
-static int clk_slow_osc_is_enabled(struct clk *clk)
+static int clk_slow_osc_is_prepared(struct clk_hw *hw)
{
- struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+ struct clk_slow_osc *osc = to_clk_slow_osc(hw);
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
@@ -115,12 +106,12 @@ static int clk_slow_osc_is_enabled(struct clk *clk)
}
static const struct clk_ops slow_osc_ops = {
- .enable = clk_slow_osc_enable,
- .disable = clk_slow_osc_disable,
- .is_enabled = clk_slow_osc_is_enabled,
+ .enable = clk_slow_osc_prepare,
+ .disable = clk_slow_osc_unprepare,
+ .is_enabled = clk_slow_osc_is_prepared,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_slow_osc(void __iomem *sckcr,
const char *name,
const char *parent_name,
@@ -128,21 +119,25 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
bool bypass,
const struct clk_slow_bits *bits)
{
- int ret;
struct clk_slow_osc *osc;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ int ret;
if (!sckcr || !name || !parent_name)
return ERR_PTR(-EINVAL);
- osc = xzalloc(sizeof(*osc));
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
- osc->clk.name = name;
- osc->clk.ops = &slow_osc_ops;
- osc->parent_name = parent_name;
- osc->clk.parent_names = &osc->parent_name;
- osc->clk.num_parents = 1;
- /* osc->clk.flags = CLK_IGNORE_UNUSED; */
+ init.name = name;
+ init.ops = &slow_osc_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_IGNORE_UNUSED;
+ osc->hw.init = &init;
osc->sckcr = sckcr;
osc->startup_usec = startup;
osc->bits = bits;
@@ -151,34 +146,35 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
writel((readl(sckcr) & ~osc->bits->cr_osc32en) |
osc->bits->cr_osc32byp, sckcr);
- ret = clk_register(&osc->clk);
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
if (ret) {
kfree(osc);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &osc->clk;
+ return hw;
}
-static void at91_clk_unregister_slow_osc(struct clk *clk)
+static void at91_clk_unregister_slow_osc(struct clk_hw *hw)
{
- struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+ struct clk_slow_osc *osc = to_clk_slow_osc(hw);
- clk_unregister(clk);
+ clk_hw_unregister(hw);
kfree(osc);
}
-static unsigned long clk_slow_rc_osc_recalc_rate(struct clk *clk,
+static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
return osc->frequency;
}
-static int clk_slow_rc_osc_enable(struct clk *clk)
+static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
void __iomem *sckcr = osc->sckcr;
writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);
@@ -188,29 +184,29 @@ static int clk_slow_rc_osc_enable(struct clk *clk)
return 0;
}
-static void clk_slow_rc_osc_disable(struct clk *clk)
+static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
void __iomem *sckcr = osc->sckcr;
writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);
}
-static int clk_slow_rc_osc_is_enabled(struct clk *clk)
+static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
return !!(readl(osc->sckcr) & osc->bits->cr_rcen);
}
static const struct clk_ops slow_rc_osc_ops = {
- .enable = clk_slow_rc_osc_enable,
- .disable = clk_slow_rc_osc_disable,
- .is_enabled = clk_slow_rc_osc_is_enabled,
+ .enable = clk_slow_rc_osc_prepare,
+ .disable = clk_slow_rc_osc_unprepare,
+ .is_enabled = clk_slow_rc_osc_is_prepared,
.recalc_rate = clk_slow_rc_osc_recalc_rate,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_slow_rc_osc(void __iomem *sckcr,
const char *name,
unsigned long frequency,
@@ -219,43 +215,51 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr,
const struct clk_slow_bits *bits)
{
struct clk_slow_rc_osc *osc;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (!sckcr || !name)
return ERR_PTR(-EINVAL);
- osc = xzalloc(sizeof(*osc));
- osc->clk.name = name;
- osc->clk.ops = &slow_rc_osc_ops;
- osc->clk.parent_names = NULL;
- osc->clk.num_parents = 0;
- /* init.flags = CLK_IGNORE_UNUSED; */
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &slow_rc_osc_ops;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ init.flags = CLK_IGNORE_UNUSED;
+ osc->hw.init = &init;
osc->sckcr = sckcr;
osc->bits = bits;
osc->frequency = frequency;
+ osc->accuracy = accuracy;
osc->startup_usec = startup;
- ret = clk_register(&osc->clk);
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
if (ret) {
kfree(osc);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &osc->clk;
+ return hw;
}
-static void at91_clk_unregister_slow_rc_osc(struct clk *clk)
+static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw)
{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
- clk_unregister(clk);
+ clk_hw_unregister(hw);
kfree(osc);
}
-static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index)
+static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
{
- struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
void __iomem *sckcr = slowck->sckcr;
u32 tmp;
@@ -280,9 +284,9 @@ static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index)
return 0;
}
-static int clk_sam9x5_slow_get_parent(struct clk *clk)
+static int clk_sam9x5_slow_get_parent(struct clk_hw *hw)
{
- struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel);
}
@@ -292,7 +296,7 @@ static const struct clk_ops sam9x5_slow_ops = {
.get_parent = clk_sam9x5_slow_get_parent,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_sam9x5_slow(void __iomem *sckcr,
const char *name,
const char **parent_names,
@@ -300,37 +304,43 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr,
const struct clk_slow_bits *bits)
{
struct clk_sam9x5_slow *slowck;
+ struct clk_hw *hw;
+ struct clk_init_data init;
int ret;
if (!sckcr || !name || !parent_names || !num_parents)
return ERR_PTR(-EINVAL);
- slowck = xzalloc(sizeof(*slowck));
- slowck->clk.name = name;
- slowck->clk.ops = &sam9x5_slow_ops;
+ slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
+ if (!slowck)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &sam9x5_slow_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = 0;
- memcpy(slowck->parent_names, parent_names,
- num_parents * sizeof(slowck->parent_names[0]));
- slowck->clk.parent_names = slowck->parent_names;
- slowck->clk.num_parents = num_parents;
+ slowck->hw.init = &init;
slowck->sckcr = sckcr;
slowck->bits = bits;
slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);
- ret = clk_register(&slowck->clk);
+ hw = &slowck->hw;
+ ret = clk_hw_register(NULL, &slowck->hw);
if (ret) {
kfree(slowck);
- return ERR_PTR(ret);
+ hw = ERR_PTR(ret);
}
- return &slowck->clk;
+ return hw;
}
-static void at91_clk_unregister_sam9x5_slow(struct clk *clk)
+static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw)
{
- struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
- clk_unregister(clk);
+ clk_hw_unregister(hw);
kfree(slowck);
}
@@ -342,7 +352,7 @@ static void __init at91sam9x5_sckc_register(struct device_node *np,
void __iomem *regbase = of_iomap(np, 0);
struct device_node *child = NULL;
const char *xtal_name;
- struct clk *slow_rc, *slow_osc, *slowck;
+ struct clk_hw *slow_rc, *slow_osc, *slowck;
bool bypass;
int ret;
@@ -385,10 +395,10 @@ static void __init at91sam9x5_sckc_register(struct device_node *np,
/* DT backward compatibility */
if (child)
- ret = of_clk_add_provider(child, of_clk_src_simple_get,
+ ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get,
slowck);
else
- ret = of_clk_add_provider(np, of_clk_src_simple_get, slowck);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
if (WARN_ON(ret))
goto unregister_slowck;
@@ -433,8 +443,8 @@ static const struct clk_slow_bits at91sam9x60_bits = {
static void __init of_sam9x60_sckc_setup(struct device_node *np)
{
void __iomem *regbase = of_iomap(np, 0);
- struct clk_onecell_data *clk_data;
- struct clk *slow_rc, *slow_osc;
+ struct clk_hw_onecell_data *clk_data;
+ struct clk_hw *slow_rc, *slow_osc;
const char *xtal_name;
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
bool bypass;
@@ -443,8 +453,9 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
if (!regbase)
return;
- slow_rc = clk_register_fixed_rate(parent_names[0], NULL, 0,
- 32768);
+ slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
+ NULL, 0, 32768,
+ 93750000);
if (IS_ERR(slow_rc))
return;
@@ -459,61 +470,54 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
if (IS_ERR(slow_osc))
goto unregister_slow_rc;
- clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL);
if (!clk_data)
goto unregister_slow_osc;
/* MD_SLCK and TD_SLCK. */
- clk_data->clk_num = 2;
- clk_data->clks = kcalloc(clk_data->clk_num,
- sizeof(*clk_data->clks), GFP_KERNEL);
- if (!clk_data->clks)
+ clk_data->num = 2;
+ clk_data->hws[0] = clk_hw_register_fixed_rate(NULL, "md_slck",
+ parent_names[0],
+ 0, 32768);
+ if (IS_ERR(clk_data->hws[0]))
goto clk_data_free;
- clk_data->clks[0] = clk_register_fixed_rate("md_slck",
- parent_names[0],
- 0, 32768);
- if (IS_ERR(clk_data->clks[0]))
- goto clks_free;
-
- clk_data->clks[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
+ clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
parent_names, 2,
&at91sam9x60_bits);
- if (IS_ERR(clk_data->clks[1]))
+ if (IS_ERR(clk_data->hws[1]))
goto unregister_md_slck;
- ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
if (WARN_ON(ret))
goto unregister_td_slck;
return;
unregister_td_slck:
- at91_clk_unregister_sam9x5_slow(clk_data->clks[1]);
+ at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);
unregister_md_slck:
- clk_unregister(clk_data->clks[0]);
-clks_free:
- kfree(clk_data->clks);
+ clk_hw_unregister(clk_data->hws[0]);
clk_data_free:
kfree(clk_data);
unregister_slow_osc:
at91_clk_unregister_slow_osc(slow_osc);
unregister_slow_rc:
- clk_unregister(slow_rc);
+ clk_hw_unregister(slow_rc);
}
CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",
of_sam9x60_sckc_setup);
-static int clk_sama5d4_slow_osc_enable(struct clk *clk)
+static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
{
- struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk);
+ struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
if (osc->prepared)
return 0;
/*
* Assume that if it has already been selected (for example by the
- * bootloader), enough time has aready passed.
+ * bootloader), enough time has already passed.
*/
if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {
osc->prepared = true;
@@ -526,16 +530,16 @@ static int clk_sama5d4_slow_osc_enable(struct clk *clk)
return 0;
}
-static int clk_sama5d4_slow_osc_is_enabled(struct clk *clk)
+static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
{
- struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk);
+ struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
return osc->prepared;
}
static const struct clk_ops sama5d4_slow_osc_ops = {
- .enable = clk_sama5d4_slow_osc_enable,
- .is_enabled = clk_sama5d4_slow_osc_is_enabled,
+ .enable = clk_sama5d4_slow_osc_prepare,
+ .is_enabled = clk_sama5d4_slow_osc_is_prepared,
};
static const struct clk_slow_bits at91sama5d4_bits = {
@@ -545,32 +549,41 @@ static const struct clk_slow_bits at91sama5d4_bits = {
static void __init of_sama5d4_sckc_setup(struct device_node *np)
{
void __iomem *regbase = of_iomap(np, 0);
- struct clk *slow_rc, *slowck;
+ struct clk_hw *slow_rc, *slowck;
struct clk_sama5d4_slow_osc *osc;
+ struct clk_init_data init;
+ const char *xtal_name;
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
int ret;
if (!regbase)
return;
- slow_rc = clk_fixed(parent_names[0], 32768);
+ slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,
+ parent_names[0],
+ NULL, 0, 32768,
+ 250000000);
if (IS_ERR(slow_rc))
return;
- osc = xzalloc(sizeof(*osc));
- osc->parent_name = of_clk_get_parent_name(np, 0);
- osc->clk.name = parent_names[1];
- osc->clk.ops = &sama5d4_slow_osc_ops;
- osc->clk.parent_names = &osc->parent_name;
- osc->clk.num_parents = 1;
+ xtal_name = of_clk_get_parent_name(np, 0);
+
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ goto unregister_slow_rc;
- /* osc->clk.flags = CLK_IGNORE_UNUSED; */
+ init.name = parent_names[1];
+ init.ops = &sama5d4_slow_osc_ops;
+ init.parent_names = &xtal_name;
+ init.num_parents = 1;
+ init.flags = CLK_IGNORE_UNUSED;
+ osc->hw.init = &init;
osc->sckcr = regbase;
osc->startup_usec = 1200000;
osc->bits = &at91sama5d4_bits;
- ret = clk_register(&osc->clk);
+ ret = clk_hw_register(NULL, &osc->hw);
if (ret)
goto free_slow_osc_data;
@@ -580,7 +593,7 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np)
if (IS_ERR(slowck))
goto unregister_slow_osc;
- ret = of_clk_add_provider(np, of_clk_src_simple_get, slowck);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
if (WARN_ON(ret))
goto unregister_slowck;
@@ -589,10 +602,11 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np)
unregister_slowck:
at91_clk_unregister_sam9x5_slow(slowck);
unregister_slow_osc:
- clk_unregister(&osc->clk);
+ clk_hw_unregister(&osc->hw);
free_slow_osc_data:
kfree(osc);
- clk_unregister(slow_rc);
+unregister_slow_rc:
+ clk_hw_unregister(slow_rc);
}
CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
of_sama5d4_sckc_setup);