diff options
Diffstat (limited to 'drivers/clk/bcm/clk-bcm2835.c')
-rw-r--r-- | drivers/clk/bcm/clk-bcm2835.c | 146 |
1 files changed, 106 insertions, 40 deletions
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, ÷r->div.hw); - if (IS_ERR(clk)) - return clk; + ret = devm_clk_hw_register(cprman->dev, ÷r->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 ÷r->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[] = { |