summaryrefslogtreecommitdiff
path: root/drivers/clk/bcm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/bcm')
-rw-r--r--drivers/clk/bcm/Kconfig30
-rw-r--r--drivers/clk/bcm/Makefile8
-rw-r--r--drivers/clk/bcm/clk-bcm2835-aux.c28
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c146
-rw-r--r--drivers/clk/bcm/clk-bcm53573-ilp.c148
-rw-r--r--drivers/clk/bcm/clk-kona-setup.c80
-rw-r--r--drivers/clk/bcm/clk-kona.c9
-rw-r--r--drivers/clk/bcm/clk-kona.h7
8 files changed, 342 insertions, 114 deletions
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
index f2878459199a..f21e9b7afd1a 100644
--- a/drivers/clk/bcm/Kconfig
+++ b/drivers/clk/bcm/Kconfig
@@ -19,8 +19,36 @@ config CLK_BCM_KONA
in the BCM281xx and BCM21664 families.
config COMMON_CLK_IPROC
- bool
+ bool "Broadcom iProc clock support"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
depends on COMMON_CLK
+ default ARCH_BCM_IPROC
help
Enable common clock framework support for Broadcom SoCs
based on the iProc architecture
+
+if COMMON_CLK_IPROC
+
+config CLK_BCM_CYGNUS
+ bool "Broadcom Cygnus clock support"
+ depends on ARCH_BCM_CYGNUS || COMPILE_TEST
+ default ARCH_BCM_CYGNUS
+ help
+ Enable common clock framework support for the Broadcom Cygnus SoC
+
+config CLK_BCM_NSP
+ bool "Broadcom Northstar/Northstar Plus clock support"
+ depends on ARCH_BCM_5301X || ARCH_BCM_NSP || COMPILE_TEST
+ default ARCH_BCM_5301X || ARCH_BCM_NSP
+ help
+ Enable common clock framework support for the Broadcom Northstar and
+ Northstar Plus SoCs
+
+config CLK_BCM_NS2
+ bool "Broadcom Northstar 2 clock support"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ default ARCH_BCM_IPROC
+ help
+ Enable common clock framework support for the Broadcom Northstar 2 SoC
+
+endif
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
index 1d79bd2c36f0..d9dc848f18c9 100644
--- a/drivers/clk/bcm/Makefile
+++ b/drivers/clk/bcm/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o
obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o
-obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o
-obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o
-obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o
-obj-$(CONFIG_ARCH_BCM_5301X) += clk-nsp.o
+obj-$(CONFIG_ARCH_BCM_53573) += clk-bcm53573-ilp.o
+obj-$(CONFIG_CLK_BCM_CYGNUS) += clk-cygnus.o
+obj-$(CONFIG_CLK_BCM_NSP) += clk-nsp.o
+obj-$(CONFIG_CLK_BCM_NS2) += clk-ns2.o
diff --git a/drivers/clk/bcm/clk-bcm2835-aux.c b/drivers/clk/bcm/clk-bcm2835-aux.c
index 3a177ade6e6c..bd750cf2238d 100644
--- a/drivers/clk/bcm/clk-bcm2835-aux.c
+++ b/drivers/clk/bcm/clk-bcm2835-aux.c
@@ -25,7 +25,7 @@
static int bcm2835_aux_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct clk_onecell_data *onecell;
+ struct clk_hw_onecell_data *onecell;
const char *parent;
struct clk *parent_clk;
struct resource *res;
@@ -41,28 +41,24 @@ static int bcm2835_aux_clk_probe(struct platform_device *pdev)
if (IS_ERR(reg))
return PTR_ERR(reg);
- onecell = devm_kmalloc(dev, sizeof(*onecell), GFP_KERNEL);
+ onecell = devm_kmalloc(dev, sizeof(*onecell) + sizeof(*onecell->hws) *
+ BCM2835_AUX_CLOCK_COUNT, GFP_KERNEL);
if (!onecell)
return -ENOMEM;
- onecell->clk_num = BCM2835_AUX_CLOCK_COUNT;
- onecell->clks = devm_kcalloc(dev, BCM2835_AUX_CLOCK_COUNT,
- sizeof(*onecell->clks), GFP_KERNEL);
- if (!onecell->clks)
- return -ENOMEM;
+ onecell->num = BCM2835_AUX_CLOCK_COUNT;
gate = reg + BCM2835_AUXENB;
- onecell->clks[BCM2835_AUX_CLOCK_UART] =
- clk_register_gate(dev, "aux_uart", parent, 0, gate, 0, 0, NULL);
-
- onecell->clks[BCM2835_AUX_CLOCK_SPI1] =
- clk_register_gate(dev, "aux_spi1", parent, 0, gate, 1, 0, NULL);
+ onecell->hws[BCM2835_AUX_CLOCK_UART] =
+ clk_hw_register_gate(dev, "aux_uart", parent, 0, gate, 0, 0, NULL);
- onecell->clks[BCM2835_AUX_CLOCK_SPI2] =
- clk_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL);
+ onecell->hws[BCM2835_AUX_CLOCK_SPI1] =
+ clk_hw_register_gate(dev, "aux_spi1", parent, 0, gate, 1, 0, NULL);
- of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, onecell);
+ onecell->hws[BCM2835_AUX_CLOCK_SPI2] =
+ clk_hw_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL);
- return 0;
+ return of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
+ onecell);
}
static const struct of_device_id bcm2835_aux_clk_of_match[] = {
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 7a7970865c2d..b68bf573dcfb 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -36,6 +36,7 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
+#include <linux/clk.h>
#include <linux/clk/bcm2835.h>
#include <linux/debugfs.h>
#include <linux/module.h>
@@ -302,8 +303,8 @@ struct bcm2835_cprman {
spinlock_t regs_lock; /* spinlock for all clocks */
const char *osc_name;
- struct clk_onecell_data onecell;
- struct clk *clks[];
+ /* Must be last */
+ struct clk_hw_onecell_data onecell;
};
static inline void cprman_write(struct bcm2835_cprman *cprman, u32 reg, u32 val)
@@ -344,24 +345,24 @@ static int bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base,
*/
void __init bcm2835_init_clocks(void)
{
- struct clk *clk;
+ struct clk_hw *hw;
int ret;
- clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 126000000);
- if (IS_ERR(clk))
+ hw = clk_hw_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 126000000);
+ if (IS_ERR(hw))
pr_err("apb_pclk not registered\n");
- clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, 0, 3000000);
- if (IS_ERR(clk))
+ hw = clk_hw_register_fixed_rate(NULL, "uart0_pclk", NULL, 0, 3000000);
+ if (IS_ERR(hw))
pr_err("uart0_pclk not registered\n");
- ret = clk_register_clkdev(clk, NULL, "20201000.uart");
+ ret = clk_hw_register_clkdev(hw, NULL, "20201000.uart");
if (ret)
pr_err("uart0_pclk alias not registered\n");
- clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, 0, 125000000);
- if (IS_ERR(clk))
+ hw = clk_hw_register_fixed_rate(NULL, "uart1_pclk", NULL, 0, 125000000);
+ if (IS_ERR(hw))
pr_err("uart1_pclk not registered\n");
- ret = clk_register_clkdev(clk, NULL, "20215000.uart");
+ ret = clk_hw_register_clkdev(hw, NULL, "20215000.uart");
if (ret)
pr_err("uart1_pclk alias not registered\n");
}
@@ -443,6 +444,8 @@ struct bcm2835_clock_data {
/* Number of fractional bits in the divider */
u32 frac_bits;
+ u32 flags;
+
bool is_vpu_clock;
bool is_mash_clock;
};
@@ -1006,16 +1009,28 @@ static int bcm2835_clock_set_rate(struct clk_hw *hw,
return 0;
}
+static bool
+bcm2835_clk_is_pllc(struct clk_hw *hw)
+{
+ if (!hw)
+ return false;
+
+ return strncmp(clk_hw_get_name(hw), "pllc", 4) == 0;
+}
+
static int bcm2835_clock_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
struct clk_hw *parent, *best_parent = NULL;
+ bool current_parent_is_pllc;
unsigned long rate, best_rate = 0;
unsigned long prate, best_prate = 0;
size_t i;
u32 div;
+ current_parent_is_pllc = bcm2835_clk_is_pllc(clk_hw_get_parent(hw));
+
/*
* Select parent clock that results in the closest but lower rate
*/
@@ -1023,6 +1038,17 @@ static int bcm2835_clock_determine_rate(struct clk_hw *hw,
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
+
+ /*
+ * Don't choose a PLLC-derived clock as our parent
+ * unless it had been manually set that way. PLLC's
+ * frequency gets adjusted by the firmware due to
+ * over-temp or under-voltage conditions, without
+ * prior notification to our clock consumer.
+ */
+ if (bcm2835_clk_is_pllc(parent) && !current_parent_is_pllc)
+ continue;
+
prate = clk_hw_get_rate(parent);
div = bcm2835_clock_choose_div(hw, req->rate, prate, true);
rate = bcm2835_clock_rate_from_divisor(clock, prate, div);
@@ -1121,11 +1147,12 @@ static const struct clk_ops bcm2835_vpu_clock_clk_ops = {
.debug_init = bcm2835_clock_debug_init,
};
-static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman,
- const struct bcm2835_pll_data *data)
+static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
+ const struct bcm2835_pll_data *data)
{
struct bcm2835_pll *pll;
struct clk_init_data init;
+ int ret;
memset(&init, 0, sizeof(init));
@@ -1144,17 +1171,20 @@ static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman,
pll->data = data;
pll->hw.init = &init;
- return devm_clk_register(cprman->dev, &pll->hw);
+ ret = devm_clk_hw_register(cprman->dev, &pll->hw);
+ if (ret)
+ return NULL;
+ return &pll->hw;
}
-static struct clk *
+static struct clk_hw *
bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
const struct bcm2835_pll_divider_data *data)
{
struct bcm2835_pll_divider *divider;
struct clk_init_data init;
- struct clk *clk;
const char *divider_name;
+ int ret;
if (data->fixed_divider != 1) {
divider_name = devm_kasprintf(cprman->dev, GFP_KERNEL,
@@ -1188,32 +1218,33 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
divider->cprman = cprman;
divider->data = data;
- clk = devm_clk_register(cprman->dev, &divider->div.hw);
- if (IS_ERR(clk))
- return clk;
+ ret = devm_clk_hw_register(cprman->dev, &divider->div.hw);
+ if (ret)
+ return ERR_PTR(ret);
/*
* PLLH's channels have a fixed divide by 10 afterwards, which
* is what our consumers are actually using.
*/
if (data->fixed_divider != 1) {
- return clk_register_fixed_factor(cprman->dev, data->name,
- divider_name,
- CLK_SET_RATE_PARENT,
- 1,
- data->fixed_divider);
+ return clk_hw_register_fixed_factor(cprman->dev, data->name,
+ divider_name,
+ CLK_SET_RATE_PARENT,
+ 1,
+ data->fixed_divider);
}
- return clk;
+ return &divider->div.hw;
}
-static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman,
+static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
const struct bcm2835_clock_data *data)
{
struct bcm2835_clock *clock;
struct clk_init_data init;
const char *parents[1 << CM_SRC_BITS];
size_t i;
+ int ret;
/*
* Replace our "xosc" references with the oscillator's
@@ -1230,13 +1261,19 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman,
init.parent_names = parents;
init.num_parents = data->num_mux_parents;
init.name = data->name;
- init.flags = CLK_IGNORE_UNUSED;
+ init.flags = data->flags | CLK_IGNORE_UNUSED;
if (data->is_vpu_clock) {
init.ops = &bcm2835_vpu_clock_clk_ops;
} else {
init.ops = &bcm2835_clock_clk_ops;
init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+ /* If the clock wasn't actually enabled at boot, it's not
+ * critical.
+ */
+ if (!(cprman_read(cprman, data->ctl_reg) & CM_ENABLE))
+ init.flags &= ~CLK_IS_CRITICAL;
}
clock = devm_kzalloc(cprman->dev, sizeof(*clock), GFP_KERNEL);
@@ -1247,7 +1284,10 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman,
clock->data = data;
clock->hw.init = &init;
- return devm_clk_register(cprman->dev, &clock->hw);
+ ret = devm_clk_hw_register(cprman->dev, &clock->hw);
+ if (ret)
+ return ERR_PTR(ret);
+ return &clock->hw;
}
static struct clk *bcm2835_register_gate(struct bcm2835_cprman *cprman,
@@ -1259,8 +1299,8 @@ static struct clk *bcm2835_register_gate(struct bcm2835_cprman *cprman,
CM_GATE_BIT, 0, &cprman->regs_lock);
}
-typedef struct clk *(*bcm2835_clk_register)(struct bcm2835_cprman *cprman,
- const void *data);
+typedef struct clk_hw *(*bcm2835_clk_register)(struct bcm2835_cprman *cprman,
+ const void *data);
struct bcm2835_clk_desc {
bcm2835_clk_register clk_register;
const void *data;
@@ -1649,6 +1689,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.div_reg = CM_VPUDIV,
.int_bits = 12,
.frac_bits = 8,
+ .flags = CLK_IS_CRITICAL,
.is_vpu_clock = true),
/* clocks with per parent mux */
@@ -1705,13 +1746,15 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.div_reg = CM_GP1DIV,
.int_bits = 12,
.frac_bits = 12,
+ .flags = CLK_IS_CRITICAL,
.is_mash_clock = true),
[BCM2835_CLOCK_GP2] = REGISTER_PER_CLK(
.name = "gp2",
.ctl_reg = CM_GP2CTL,
.div_reg = CM_GP2DIV,
.int_bits = 12,
- .frac_bits = 12),
+ .frac_bits = 12,
+ .flags = CLK_IS_CRITICAL),
/* HDMI state machine */
[BCM2835_CLOCK_HSM] = REGISTER_PER_CLK(
@@ -1790,18 +1833,38 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_PERIICTL),
};
+/*
+ * Permanently take a reference on the parent of the SDRAM clock.
+ *
+ * While the SDRAM is being driven by its dedicated PLL most of the
+ * time, there is a little loop running in the firmware that
+ * periodically switches the SDRAM to using our CM clock to do PVT
+ * recalibration, with the assumption that the previously configured
+ * SDRAM parent is still enabled and running.
+ */
+static int bcm2835_mark_sdc_parent_critical(struct clk *sdc)
+{
+ struct clk *parent = clk_get_parent(sdc);
+
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
+ return clk_prepare_enable(parent);
+}
+
static int bcm2835_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct clk **clks;
+ struct clk_hw **hws;
struct bcm2835_cprman *cprman;
struct resource *res;
const struct bcm2835_clk_desc *desc;
const size_t asize = ARRAY_SIZE(clk_desc_array);
size_t i;
+ int ret;
- cprman = devm_kzalloc(dev,
- sizeof(*cprman) + asize * sizeof(*clks),
+ cprman = devm_kzalloc(dev, sizeof(*cprman) +
+ sizeof(*cprman->onecell.hws) * asize,
GFP_KERNEL);
if (!cprman)
return -ENOMEM;
@@ -1819,18 +1882,21 @@ static int bcm2835_clk_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, cprman);
- cprman->onecell.clk_num = asize;
- cprman->onecell.clks = cprman->clks;
- clks = cprman->clks;
+ cprman->onecell.num = asize;
+ hws = cprman->onecell.hws;
for (i = 0; i < asize; i++) {
desc = &clk_desc_array[i];
if (desc->clk_register && desc->data)
- clks[i] = desc->clk_register(cprman, desc->data);
+ hws[i] = desc->clk_register(cprman, desc->data);
}
- return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
- &cprman->onecell);
+ ret = bcm2835_mark_sdc_parent_critical(hws[BCM2835_CLOCK_SDRAM]->clk);
+ if (ret)
+ return ret;
+
+ return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+ &cprman->onecell);
}
static const struct of_device_id bcm2835_clk_of_match[] = {
diff --git a/drivers/clk/bcm/clk-bcm53573-ilp.c b/drivers/clk/bcm/clk-bcm53573-ilp.c
new file mode 100644
index 000000000000..36eb3716ffb0
--- /dev/null
+++ b/drivers/clk/bcm/clk-bcm53573-ilp.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define PMU_XTAL_FREQ_RATIO 0x66c
+#define XTAL_ALP_PER_4ILP 0x00001fff
+#define XTAL_CTL_EN 0x80000000
+#define PMU_SLOW_CLK_PERIOD 0x6dc
+
+struct bcm53573_ilp {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+static int bcm53573_ilp_enable(struct clk_hw *hw)
+{
+ struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
+
+ regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0x10199);
+ regmap_write(ilp->regmap, 0x674, 0x10000);
+
+ return 0;
+}
+
+static void bcm53573_ilp_disable(struct clk_hw *hw)
+{
+ struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
+
+ regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0);
+ regmap_write(ilp->regmap, 0x674, 0);
+}
+
+static unsigned long bcm53573_ilp_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
+ struct regmap *regmap = ilp->regmap;
+ u32 last_val, cur_val;
+ int sum = 0, num = 0, loop_num = 0;
+ int avg;
+
+ /* Enable measurement */
+ regmap_write(regmap, PMU_XTAL_FREQ_RATIO, XTAL_CTL_EN);
+
+ /* Read initial value */
+ regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &last_val);
+ last_val &= XTAL_ALP_PER_4ILP;
+
+ /*
+ * At minimum we should loop for a bit to let hardware do the
+ * measurement. This isn't very accurate however, so for a better
+ * precision lets try getting 20 different values for and use average.
+ */
+ while (num < 20) {
+ regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &cur_val);
+ cur_val &= XTAL_ALP_PER_4ILP;
+
+ if (cur_val != last_val) {
+ /* Got different value, use it */
+ sum += cur_val;
+ num++;
+ loop_num = 0;
+ last_val = cur_val;
+ } else if (++loop_num > 5000) {
+ /* Same value over and over, give up */
+ sum += cur_val;
+ num++;
+ break;
+ }
+
+ cpu_relax();
+ }
+
+ /* Disable measurement to save power */
+ regmap_write(regmap, PMU_XTAL_FREQ_RATIO, 0x0);
+
+ avg = sum / num;
+
+ return parent_rate * 4 / avg;
+}
+
+static const struct clk_ops bcm53573_ilp_clk_ops = {
+ .enable = bcm53573_ilp_enable,
+ .disable = bcm53573_ilp_disable,
+ .recalc_rate = bcm53573_ilp_recalc_rate,
+};
+
+static void bcm53573_ilp_init(struct device_node *np)
+{
+ struct bcm53573_ilp *ilp;
+ struct clk_init_data init = { };
+ const char *parent_name;
+ int err;
+
+ ilp = kzalloc(sizeof(*ilp), GFP_KERNEL);
+ if (!ilp)
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name) {
+ err = -ENOENT;
+ goto err_free_ilp;
+ }
+
+ ilp->regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(ilp->regmap)) {
+ err = PTR_ERR(ilp->regmap);
+ goto err_free_ilp;
+ }
+
+ init.name = np->name;
+ init.ops = &bcm53573_ilp_clk_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ ilp->hw.init = &init;
+ err = clk_hw_register(NULL, &ilp->hw);
+ if (err)
+ goto err_free_ilp;
+
+ err = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &ilp->hw);
+ if (err)
+ goto err_clk_hw_unregister;
+
+ return;
+
+err_clk_hw_unregister:
+ clk_hw_unregister(&ilp->hw);
+err_free_ilp:
+ kfree(ilp);
+ pr_err("Failed to init ILP clock: %d\n", err);
+}
+
+/* We need it very early for arch code, before device model gets ready */
+CLK_OF_DECLARE(bcm53573_ilp_clk, "brcm,bcm53573-ilp", bcm53573_ilp_init);
diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c
index 526b0b0e9a9f..c37a7f0e83aa 100644
--- a/drivers/clk/bcm/clk-kona-setup.c
+++ b/drivers/clk/bcm/clk-kona-setup.c
@@ -586,8 +586,8 @@ static u32 *parent_process(const char *clocks[],
}
/* There is at least one parent, so allocate a selector array */
-
- parent_sel = kmalloc(parent_count * sizeof(*parent_sel), GFP_KERNEL);
+ parent_sel = kmalloc_array(parent_count, sizeof(*parent_sel),
+ GFP_KERNEL);
if (!parent_sel) {
pr_err("%s: error allocating %u parent selectors\n", __func__,
parent_count);
@@ -696,77 +696,69 @@ static void bcm_clk_teardown(struct kona_clk *bcm_clk)
bcm_clk->type = bcm_clk_none;
}
-static void kona_clk_teardown(struct clk *clk)
+static void kona_clk_teardown(struct clk_hw *hw)
{
- struct clk_hw *hw;
struct kona_clk *bcm_clk;
- if (!clk)
+ if (!hw)
return;
- hw = __clk_get_hw(clk);
- if (!hw) {
- pr_err("%s: clk %p has null hw pointer\n", __func__, clk);
- return;
- }
- clk_unregister(clk);
+ clk_hw_unregister(hw);
bcm_clk = to_kona_clk(hw);
bcm_clk_teardown(bcm_clk);
}
-struct clk *kona_clk_setup(struct kona_clk *bcm_clk)
+static int kona_clk_setup(struct kona_clk *bcm_clk)
{
+ int ret;
struct clk_init_data *init_data = &bcm_clk->init_data;
- struct clk *clk = NULL;
switch (bcm_clk->type) {
case bcm_clk_peri:
- if (peri_clk_setup(bcm_clk->u.data, init_data))
- return NULL;
+ ret = peri_clk_setup(bcm_clk->u.data, init_data);
+ if (ret)
+ return ret;
break;
default:
pr_err("%s: clock type %d invalid for %s\n", __func__,
(int)bcm_clk->type, init_data->name);
- return NULL;
+ return -EINVAL;
}
/* Make sure everything makes sense before we set it up */
if (!kona_clk_valid(bcm_clk)) {
pr_err("%s: clock data invalid for %s\n", __func__,
init_data->name);
+ ret = -EINVAL;
goto out_teardown;
}
bcm_clk->hw.init = init_data;
- clk = clk_register(NULL, &bcm_clk->hw);
- if (IS_ERR(clk)) {
- pr_err("%s: error registering clock %s (%ld)\n", __func__,
- init_data->name, PTR_ERR(clk));
+ ret = clk_hw_register(NULL, &bcm_clk->hw);
+ if (ret) {
+ pr_err("%s: error registering clock %s (%d)\n", __func__,
+ init_data->name, ret);
goto out_teardown;
}
- BUG_ON(!clk);
- return clk;
+ return 0;
out_teardown:
bcm_clk_teardown(bcm_clk);
- return NULL;
+ return ret;
}
static void ccu_clks_teardown(struct ccu_data *ccu)
{
u32 i;
- for (i = 0; i < ccu->clk_data.clk_num; i++)
- kona_clk_teardown(ccu->clk_data.clks[i]);
- kfree(ccu->clk_data.clks);
+ for (i = 0; i < ccu->clk_num; i++)
+ kona_clk_teardown(&ccu->kona_clks[i].hw);
}
static void kona_ccu_teardown(struct ccu_data *ccu)
{
- kfree(ccu->clk_data.clks);
- ccu->clk_data.clks = NULL;
if (!ccu->base)
return;
@@ -793,6 +785,20 @@ static bool ccu_data_valid(struct ccu_data *ccu)
return true;
}
+static struct clk_hw *
+of_clk_kona_onecell_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct ccu_data *ccu = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= ccu->clk_num) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &ccu->kona_clks[idx].hw;
+}
+
/*
* Set up a CCU. Call the provided ccu_clks_setup callback to
* initialize the array of clocks provided by the CCU.
@@ -805,18 +811,6 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu,
unsigned int i;
int ret;
- if (ccu->clk_data.clk_num) {
- size_t size;
-
- size = ccu->clk_data.clk_num * sizeof(*ccu->clk_data.clks);
- ccu->clk_data.clks = kzalloc(size, GFP_KERNEL);
- if (!ccu->clk_data.clks) {
- pr_err("%s: unable to allocate %u clocks for %s\n",
- __func__, ccu->clk_data.clk_num, node->name);
- return;
- }
- }
-
ret = of_address_to_resource(node, 0, &res);
if (ret) {
pr_err("%s: no valid CCU registers found for %s\n", __func__,
@@ -851,13 +845,13 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu,
* the clock framework clock array (in ccu->data). Then
* register as a provider for these clocks.
*/
- for (i = 0; i < ccu->clk_data.clk_num; i++) {
+ for (i = 0; i < ccu->clk_num; i++) {
if (!ccu->kona_clks[i].ccu)
continue;
- ccu->clk_data.clks[i] = kona_clk_setup(&ccu->kona_clks[i]);
+ kona_clk_setup(&ccu->kona_clks[i]);
}
- ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->clk_data);
+ ret = of_clk_add_hw_provider(node, of_clk_kona_onecell_get, ccu);
if (ret) {
pr_err("%s: error adding ccu %s as provider (%d)\n", __func__,
node->name, ret);
diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c
index 3a15347b4233..eee64b9e5d10 100644
--- a/drivers/clk/bcm/clk-kona.c
+++ b/drivers/clk/bcm/clk-kona.c
@@ -1256,19 +1256,18 @@ bool __init kona_ccu_init(struct ccu_data *ccu)
{
unsigned long flags;
unsigned int which;
- struct clk **clks = ccu->clk_data.clks;
struct kona_clk *kona_clks = ccu->kona_clks;
bool success = true;
flags = ccu_lock(ccu);
__ccu_write_enable(ccu);
- for (which = 0; which < ccu->clk_data.clk_num; which++) {
- struct kona_clk *bcm_clk;
+ for (which = 0; which < ccu->clk_num; which++) {
+ struct kona_clk *bcm_clk = &kona_clks[which];
- if (!clks[which])
+ if (!bcm_clk->ccu)
continue;
- bcm_clk = &kona_clks[which];
+
success &= __kona_clk_init(bcm_clk);
}
diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h
index 906576ec97b6..f4b39bb5558a 100644
--- a/drivers/clk/bcm/clk-kona.h
+++ b/drivers/clk/bcm/clk-kona.h
@@ -481,7 +481,7 @@ struct ccu_data {
bool write_enabled; /* write access is currently enabled */
struct ccu_policy policy;
struct device_node *node;
- struct clk_onecell_data clk_data;
+ size_t clk_num;
const char *name;
u32 range; /* byte range of address space */
struct kona_clk kona_clks[]; /* must be last */
@@ -491,9 +491,7 @@ struct ccu_data {
#define KONA_CCU_COMMON(_prefix, _name, _ccuname) \
.name = #_name "_ccu", \
.lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \
- .clk_data = { \
- .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \
- }
+ .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT
/* Exported globals */
@@ -505,7 +503,6 @@ extern u64 scaled_div_max(struct bcm_clk_div *div);
extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value,
u32 billionths);
-extern struct clk *kona_clk_setup(struct kona_clk *bcm_clk);
extern void __init kona_dt_ccu_setup(struct ccu_data *ccu,
struct device_node *node);
extern bool __init kona_ccu_init(struct ccu_data *ccu);