Merge branches 'clk-renesas', 'clk-determine-rate', 'clk-allwinner', 'clk-samsung' and 'clk-amlogic' into clk-next

- Make clk_ops::determine_rate mandatory for muxes

* clk-renesas:
  clk: renesas: rzg2l: Convert to readl_poll_timeout_atomic()
  clk: renesas: mstp: Convert to readl_poll_timeout_atomic()
  clk: renesas: cpg-mssr: Convert to readl_poll_timeout_atomic()
  iopoll: Do not use timekeeping in read_poll_timeout_atomic()
  iopoll: Call cpu_relax() in busy loops
  clk: renesas: rzg2l: Fix CPG_SIPLL5_CLK1 register write
  clk: renesas: r8a779a0: Add PWM clock

* clk-determine-rate: (71 commits)
  clk: sprd: composite: Simplify determine_rate implementation
  ASoC: tlv320aic32x4: pll: Remove impossible condition in clk_aic32x4_pll_determine_rate()
  clk: Fix best_parent_rate after moving code into a separate function
  clk: Forbid to register a mux without determine_rate
  ASoC: tlv320aic32x4: div: Switch to determine_rate
  ASoC: tlv320aic32x4: pll: Switch to determine_rate
  clk: tegra: super: Switch to determine_rate
  clk: tegra: periph: Switch to determine_rate
  clk: stm32: composite: Switch to determine_rate
  clk: st: flexgen: Switch to determine_rate
  clk: sprd: composite: Switch to determine_rate
  clk: ingenic: tcu: Switch to determine_rate
  clk: ingenic: cgu: Switch to determine_rate
  clk: imx: scu: Switch to determine_rate
  clk: da8xx: clk48: Switch to determine_rate
  clk: si5351: clkout: Switch to determine_rate
  clk: si5351: msynth: Switch to determine_rate
  clk: si5351: pll: Switch to determine_rate
  clk: si5341: Switch to determine_rate
  clk: cdce706: clkout: Switch to determine_rate
  ...

* clk-allwinner:
  clk: sunxi-ng: a64: force select PLL_MIPI in TCON0 mux

* clk-samsung:
  clk: samsung: add CONFIG_OF dependency
  clk: samsung: Re-add support for Exynos4212 CPU clock
  clk: samsung: Add Exynos4212 compatible to CLKOUT driver
  dt-bindings: clock: samsung,exynos: add Exynos4212 clock compatible

* clk-amlogic:
  MAINTAINERS: repair pattern in ARM/Amlogic Meson SoC CLOCK FRAMEWORK
  clk: meson: pll: remove unneeded semicolon
  clk: meson: a1: Staticize rtc clk
  clk: meson: a1: add Amlogic A1 Peripherals clock controller driver
  clk: meson: a1: add Amlogic A1 PLL clock controller driver
  clk: meson: introduce new pll power-on sequence for A1 SoC family
  clk: meson: make pll rst bit as optional
  dt-bindings: clock: meson: add A1 Peripherals clock controller bindings
  dt-bindings: clock: meson: add A1 PLL clock controller bindings
This commit is contained in:
Stephen Boyd 2023-06-26 08:55:04 -07:00
72 changed files with 3713 additions and 283 deletions

View file

@ -0,0 +1,73 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/amlogic,a1-peripherals-clkc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Amlogic A1 Peripherals Clock Control Unit
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
- Jerome Brunet <jbrunet@baylibre.com>
- Jian Hu <jian.hu@jian.hu.com>
- Dmitry Rokosov <ddrokosov@sberdevices.ru>
properties:
compatible:
const: amlogic,a1-peripherals-clkc
'#clock-cells':
const: 1
reg:
maxItems: 1
clocks:
items:
- description: input fixed pll div2
- description: input fixed pll div3
- description: input fixed pll div5
- description: input fixed pll div7
- description: input hifi pll
- description: input oscillator (usually at 24MHz)
clock-names:
items:
- const: fclk_div2
- const: fclk_div3
- const: fclk_div5
- const: fclk_div7
- const: hifi_pll
- const: xtal
required:
- compatible
- '#clock-cells'
- reg
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
apb {
#address-cells = <2>;
#size-cells = <2>;
clock-controller@800 {
compatible = "amlogic,a1-peripherals-clkc";
reg = <0 0x800 0 0x104>;
#clock-cells = <1>;
clocks = <&clkc_pll CLKID_FCLK_DIV2>,
<&clkc_pll CLKID_FCLK_DIV3>,
<&clkc_pll CLKID_FCLK_DIV5>,
<&clkc_pll CLKID_FCLK_DIV7>,
<&clkc_pll CLKID_HIFI_PLL>,
<&xtal>;
clock-names = "fclk_div2", "fclk_div3",
"fclk_div5", "fclk_div7",
"hifi_pll", "xtal";
};
};

View file

@ -0,0 +1,59 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/amlogic,a1-pll-clkc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Amlogic A1 PLL Clock Control Unit
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
- Jerome Brunet <jbrunet@baylibre.com>
- Jian Hu <jian.hu@jian.hu.com>
- Dmitry Rokosov <ddrokosov@sberdevices.ru>
properties:
compatible:
const: amlogic,a1-pll-clkc
'#clock-cells':
const: 1
reg:
maxItems: 1
clocks:
items:
- description: input fixpll_in
- description: input hifipll_in
clock-names:
items:
- const: fixpll_in
- const: hifipll_in
required:
- compatible
- '#clock-cells'
- reg
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h>
apb {
#address-cells = <2>;
#size-cells = <2>;
clock-controller@7c80 {
compatible = "amlogic,a1-pll-clkc";
reg = <0 0x7c80 0 0x18c>;
#clock-cells = <1>;
clocks = <&clkc_periphs CLKID_FIXPLL_IN>,
<&clkc_periphs CLKID_HIFIPLL_IN>;
clock-names = "fixpll_in", "hifipll_in";
};
};

View file

@ -24,6 +24,7 @@ properties:
- samsung,exynos3250-cmu-dmc
- samsung,exynos3250-cmu-isp
- samsung,exynos4210-clock
- samsung,exynos4212-clock
- samsung,exynos4412-clock
- samsung,exynos5250-clock
- items:

View file

@ -1888,6 +1888,7 @@ L: linux-amlogic@lists.infradead.org
S: Maintained
F: Documentation/devicetree/bindings/clock/amlogic*
F: drivers/clk/meson/
F: include/dt-bindings/clock/amlogic,a1*
F: include/dt-bindings/clock/gxbb*
F: include/dt-bindings/clock/meson*

View file

@ -53,13 +53,19 @@ static int owl_comp_is_enabled(struct clk_hw *hw)
return owl_gate_clk_is_enabled(common, &comp->gate_hw);
}
static long owl_comp_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int owl_comp_div_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct owl_composite *comp = hw_to_owl_comp(hw);
long rate;
return owl_divider_helper_round_rate(&comp->common, &comp->rate.div_hw,
rate, parent_rate);
rate = owl_divider_helper_round_rate(&comp->common, &comp->rate.div_hw,
req->rate, &req->best_parent_rate);
if (rate < 0)
return rate;
req->rate = rate;
return 0;
}
static unsigned long owl_comp_div_recalc_rate(struct clk_hw *hw,
@ -80,14 +86,20 @@ static int owl_comp_div_set_rate(struct clk_hw *hw, unsigned long rate,
rate, parent_rate);
}
static long owl_comp_fact_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int owl_comp_fact_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct owl_composite *comp = hw_to_owl_comp(hw);
long rate;
return owl_factor_helper_round_rate(&comp->common,
&comp->rate.factor_hw,
rate, parent_rate);
rate = owl_factor_helper_round_rate(&comp->common,
&comp->rate.factor_hw,
req->rate, &req->best_parent_rate);
if (rate < 0)
return rate;
req->rate = rate;
return 0;
}
static unsigned long owl_comp_fact_recalc_rate(struct clk_hw *hw,
@ -152,7 +164,7 @@ const struct clk_ops owl_comp_div_ops = {
.is_enabled = owl_comp_is_enabled,
/* div_ops */
.round_rate = owl_comp_div_round_rate,
.determine_rate = owl_comp_div_determine_rate,
.recalc_rate = owl_comp_div_recalc_rate,
.set_rate = owl_comp_div_set_rate,
};
@ -169,7 +181,7 @@ const struct clk_ops owl_comp_fact_ops = {
.is_enabled = owl_comp_is_enabled,
/* fact_ops */
.round_rate = owl_comp_fact_round_rate,
.determine_rate = owl_comp_fact_determine_rate,
.recalc_rate = owl_comp_fact_recalc_rate,
.set_rate = owl_comp_fact_set_rate,
};
@ -189,6 +201,7 @@ const struct clk_ops owl_comp_fix_fact_ops = {
const struct clk_ops owl_comp_pass_ops = {
/* mux_ops */
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = owl_comp_get_parent,
.set_parent = owl_comp_set_parent,

View file

@ -533,6 +533,7 @@ static const struct clk_ops sam9x5_main_ops = {
.prepare = clk_sam9x5_main_prepare,
.is_prepared = clk_sam9x5_main_is_prepared,
.recalc_rate = clk_sam9x5_main_recalc_rate,
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = clk_sam9x5_main_set_parent,
.get_parent = clk_sam9x5_main_get_parent,
.save_context = clk_sam9x5_main_save_context,

View file

@ -36,26 +36,31 @@ static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk_hw *hw,
return parent_rate / (smddiv + 1);
}
static long at91sam9x5_clk_smd_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int at91sam9x5_clk_smd_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
unsigned long div;
unsigned long bestrate;
unsigned long tmp;
if (rate >= *parent_rate)
return *parent_rate;
if (req->rate >= req->best_parent_rate) {
req->rate = req->best_parent_rate;
return 0;
}
div = *parent_rate / rate;
if (div > SMD_MAX_DIV)
return *parent_rate / (SMD_MAX_DIV + 1);
div = req->best_parent_rate / req->rate;
if (div > SMD_MAX_DIV) {
req->rate = req->best_parent_rate / (SMD_MAX_DIV + 1);
return 0;
}
bestrate = *parent_rate / div;
tmp = *parent_rate / (div + 1);
if (bestrate - rate > rate - tmp)
bestrate = req->best_parent_rate / div;
tmp = req->best_parent_rate / (div + 1);
if (bestrate - req->rate > req->rate - tmp)
bestrate = tmp;
return bestrate;
req->rate = bestrate;
return 0;
}
static int at91sam9x5_clk_smd_set_parent(struct clk_hw *hw, u8 index)
@ -98,7 +103,7 @@ static int at91sam9x5_clk_smd_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops at91sam9x5_smd_ops = {
.recalc_rate = at91sam9x5_clk_smd_recalc_rate,
.round_rate = at91sam9x5_clk_smd_round_rate,
.determine_rate = at91sam9x5_clk_smd_determine_rate,
.get_parent = at91sam9x5_clk_smd_get_parent,
.set_parent = at91sam9x5_clk_smd_set_parent,
.set_rate = at91sam9x5_clk_smd_set_rate,

View file

@ -310,6 +310,7 @@ static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
}
static const struct clk_ops sam9x5_slow_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = clk_sam9x5_slow_set_parent,
.get_parent = clk_sam9x5_slow_get_parent,
};

View file

@ -210,6 +210,7 @@ static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
}
static const struct clk_ops berlin2_div_rate_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.recalc_rate = berlin2_div_recalc_rate,
};

View file

@ -384,23 +384,25 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
return 0;
}
static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int axi_clkgen_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(hw);
const struct axi_clkgen_limits *limits = &axi_clkgen->limits;
unsigned int d, m, dout;
unsigned long long tmp;
axi_clkgen_calc_params(limits, *parent_rate, rate, &d, &m, &dout);
axi_clkgen_calc_params(limits, req->best_parent_rate, req->rate,
&d, &m, &dout);
if (d == 0 || dout == 0 || m == 0)
return -EINVAL;
tmp = (unsigned long long)*parent_rate * m;
tmp = (unsigned long long)req->best_parent_rate * m;
tmp = DIV_ROUND_CLOSEST_ULL(tmp, dout * d);
return min_t(unsigned long long, tmp, LONG_MAX);
req->rate = min_t(unsigned long long, tmp, LONG_MAX);
return 0;
}
static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen,
@ -495,7 +497,7 @@ static u8 axi_clkgen_get_parent(struct clk_hw *clk_hw)
static const struct clk_ops axi_clkgen_ops = {
.recalc_rate = axi_clkgen_recalc_rate,
.round_rate = axi_clkgen_round_rate,
.determine_rate = axi_clkgen_determine_rate,
.set_rate = axi_clkgen_set_rate,
.enable = axi_clkgen_enable,
.disable = axi_clkgen_disable,

View file

@ -155,6 +155,7 @@ static u8 cdce706_clkin_get_parent(struct clk_hw *hw)
}
static const struct clk_ops cdce706_clkin_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = cdce706_clkin_set_parent,
.get_parent = cdce706_clkin_get_parent,
};
@ -287,18 +288,19 @@ static unsigned long cdce706_divider_recalc_rate(struct clk_hw *hw,
return 0;
}
static long cdce706_divider_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int cdce706_divider_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct cdce706_hw_data *hwd = to_hw_data(hw);
struct cdce706_dev_data *cdce = hwd->dev_data;
unsigned long rate = req->rate;
unsigned long mul, div;
dev_dbg(&hwd->dev_data->client->dev,
"%s, rate: %lu, parent_rate: %lu\n",
__func__, rate, *parent_rate);
__func__, rate, req->best_parent_rate);
rational_best_approximation(rate, *parent_rate,
rational_best_approximation(rate, req->best_parent_rate,
1, CDCE706_DIVIDER_DIVIDER_MAX,
&mul, &div);
if (!mul)
@ -343,8 +345,8 @@ static long cdce706_divider_round_rate(struct clk_hw *hw, unsigned long rate,
dev_dbg(&hwd->dev_data->client->dev,
"%s, altering parent rate: %lu -> %lu\n",
__func__, *parent_rate, rate * div);
*parent_rate = rate * div;
__func__, req->best_parent_rate, rate * div);
req->best_parent_rate = rate * div;
}
hwd->div = div;
@ -352,7 +354,8 @@ static long cdce706_divider_round_rate(struct clk_hw *hw, unsigned long rate,
"%s, divider: %d, div: %lu\n",
__func__, hwd->idx, div);
return *parent_rate / div;
req->rate = req->best_parent_rate / div;
return 0;
}
static int cdce706_divider_set_rate(struct clk_hw *hw, unsigned long rate,
@ -374,7 +377,7 @@ static const struct clk_ops cdce706_divider_ops = {
.set_parent = cdce706_divider_set_parent,
.get_parent = cdce706_divider_get_parent,
.recalc_rate = cdce706_divider_recalc_rate,
.round_rate = cdce706_divider_round_rate,
.determine_rate = cdce706_divider_determine_rate,
.set_rate = cdce706_divider_set_rate,
};
@ -420,11 +423,12 @@ static unsigned long cdce706_clkout_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
static long cdce706_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int cdce706_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
*parent_rate = rate;
return rate;
req->best_parent_rate = req->rate;
return 0;
}
static int cdce706_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@ -439,7 +443,7 @@ static const struct clk_ops cdce706_clkout_ops = {
.set_parent = cdce706_clkout_set_parent,
.get_parent = cdce706_clkout_get_parent,
.recalc_rate = cdce706_clkout_recalc_rate,
.round_rate = cdce706_clkout_round_rate,
.determine_rate = cdce706_clkout_determine_rate,
.set_rate = cdce706_clkout_set_rate,
};

View file

@ -537,6 +537,7 @@ static const struct clk_ops k210_pll2_ops = {
.disable = k210_pll_disable,
.is_enabled = k210_pll_is_enabled,
.recalc_rate = k210_pll_get_rate,
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = k210_pll2_set_parent,
.get_parent = k210_pll2_get_parent,
};
@ -635,6 +636,7 @@ static unsigned long k210_aclk_get_rate(struct clk_hw *hw,
}
static const struct clk_ops k210_aclk_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = k210_aclk_set_parent,
.get_parent = k210_aclk_get_parent,
.recalc_rate = k210_aclk_get_rate,
@ -774,6 +776,7 @@ static unsigned long k210_clk_get_rate(struct clk_hw *hw,
static const struct clk_ops k210_clk_mux_ops = {
.enable = k210_clk_enable,
.disable = k210_clk_disable,
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = k210_clk_set_parent,
.get_parent = k210_clk_get_parent,
.recalc_rate = k210_clk_get_rate,

View file

@ -103,22 +103,6 @@ static int lan966x_gck_set_rate(struct clk_hw *hw,
return 0;
}
static long lan966x_gck_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned int div;
if (rate == 0 || *parent_rate == 0)
return -EINVAL;
if (rate >= *parent_rate)
return *parent_rate;
div = DIV_ROUND_CLOSEST(*parent_rate, rate);
return *parent_rate / div;
}
static unsigned long lan966x_gck_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@ -177,7 +161,6 @@ static const struct clk_ops lan966x_gck_ops = {
.enable = lan966x_gck_enable,
.disable = lan966x_gck_disable,
.set_rate = lan966x_gck_set_rate,
.round_rate = lan966x_gck_round_rate,
.recalc_rate = lan966x_gck_recalc_rate,
.determine_rate = lan966x_gck_determine_rate,
.set_parent = lan966x_gck_set_parent,

View file

@ -1279,6 +1279,7 @@ static const struct clk_ops lmk04832_clkout_ops = {
.is_enabled = lmk04832_clkout_is_enabled,
.prepare = lmk04832_clkout_prepare,
.unprepare = lmk04832_clkout_unprepare,
.determine_rate = __clk_mux_determine_rate,
.set_parent = lmk04832_clkout_set_parent,
.get_parent = lmk04832_clkout_get_parent,
};

View file

@ -209,6 +209,7 @@ static u8 lochnagar_clk_get_parent(struct clk_hw *hw)
static const struct clk_ops lochnagar_clk_ops = {
.prepare = lochnagar_clk_prepare,
.unprepare = lochnagar_clk_unprepare,
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = lochnagar_clk_set_parent,
.get_parent = lochnagar_clk_get_parent,
};

View file

@ -878,6 +878,7 @@ static u8 mux_get_parent(struct clk_hw *hw)
}
static const struct clk_ops cmux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = mux_get_parent,
.set_parent = mux_set_parent,
};

View file

@ -551,6 +551,7 @@ static int si5341_clk_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops si5341_clk_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = si5341_clk_set_parent,
.get_parent = si5341_clk_get_parent,
.recalc_rate = si5341_clk_recalc_rate,
@ -827,19 +828,20 @@ static unsigned long si5341_output_clk_recalc_rate(struct clk_hw *hw,
return parent_rate / r_divider;
}
static long si5341_output_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int si5341_output_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
unsigned long rate = req->rate;
unsigned long r;
if (!rate)
return 0;
r = *parent_rate >> 1;
r = req->best_parent_rate >> 1;
/* If rate is an even divisor, no changes to parent required */
if (r && !(r % rate))
return (long)rate;
return 0;
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
if (rate > 200000000) {
@ -849,14 +851,15 @@ static long si5341_output_clk_round_rate(struct clk_hw *hw, unsigned long rate,
/* Take a parent frequency near 400 MHz */
r = (400000000u / rate) & ~1;
}
*parent_rate = r * rate;
req->best_parent_rate = r * rate;
} else {
/* We cannot change our parent's rate, report what we can do */
r /= rate;
rate = *parent_rate / (r << 1);
rate = req->best_parent_rate / (r << 1);
}
return rate;
req->rate = rate;
return 0;
}
static int si5341_output_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@ -929,7 +932,7 @@ static const struct clk_ops si5341_output_clk_ops = {
.prepare = si5341_output_clk_prepare,
.unprepare = si5341_output_clk_unprepare,
.recalc_rate = si5341_output_clk_recalc_rate,
.round_rate = si5341_output_clk_round_rate,
.determine_rate = si5341_output_clk_determine_rate,
.set_rate = si5341_output_clk_set_rate,
.set_parent = si5341_output_set_parent,
.get_parent = si5341_output_get_parent,

View file

@ -442,11 +442,12 @@ static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw,
return (unsigned long)rate;
}
static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int si5351_pll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct si5351_hw_data *hwdata =
container_of(hw, struct si5351_hw_data, hw);
unsigned long rate = req->rate;
unsigned long rfrac, denom, a, b, c;
unsigned long long lltmp;
@ -456,18 +457,18 @@ static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
rate = SI5351_PLL_VCO_MAX;
/* determine integer part of feedback equation */
a = rate / *parent_rate;
a = rate / req->best_parent_rate;
if (a < SI5351_PLL_A_MIN)
rate = *parent_rate * SI5351_PLL_A_MIN;
rate = req->best_parent_rate * SI5351_PLL_A_MIN;
if (a > SI5351_PLL_A_MAX)
rate = *parent_rate * SI5351_PLL_A_MAX;
rate = req->best_parent_rate * SI5351_PLL_A_MAX;
/* find best approximation for b/c = fVCO mod fIN */
denom = 1000 * 1000;
lltmp = rate % (*parent_rate);
lltmp = rate % (req->best_parent_rate);
lltmp *= denom;
do_div(lltmp, *parent_rate);
do_div(lltmp, req->best_parent_rate);
rfrac = (unsigned long)lltmp;
b = 0;
@ -484,19 +485,20 @@ static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate,
hwdata->params.p1 -= 512;
/* recalculate rate by fIN * (a + b/c) */
lltmp = *parent_rate;
lltmp = req->best_parent_rate;
lltmp *= b;
do_div(lltmp, c);
rate = (unsigned long)lltmp;
rate += *parent_rate * a;
rate += req->best_parent_rate * a;
dev_dbg(&hwdata->drvdata->client->dev,
"%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n",
__func__, clk_hw_get_name(hw), a, b, c,
*parent_rate, rate);
req->best_parent_rate, rate);
return rate;
req->rate = rate;
return 0;
}
static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@ -533,7 +535,7 @@ static const struct clk_ops si5351_pll_ops = {
.set_parent = si5351_pll_set_parent,
.get_parent = si5351_pll_get_parent,
.recalc_rate = si5351_pll_recalc_rate,
.round_rate = si5351_pll_round_rate,
.determine_rate = si5351_pll_determine_rate,
.set_rate = si5351_pll_set_rate,
};
@ -640,11 +642,12 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
return (unsigned long)rate;
}
static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int si5351_msynth_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct si5351_hw_data *hwdata =
container_of(hw, struct si5351_hw_data, hw);
unsigned long rate = req->rate;
unsigned long long lltmp;
unsigned long a, b, c;
int divby4;
@ -679,10 +682,10 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
b = 0;
c = 1;
*parent_rate = a * rate;
req->best_parent_rate = a * rate;
} else if (hwdata->num >= 6) {
/* determine the closest integer divider */
a = DIV_ROUND_CLOSEST(*parent_rate, rate);
a = DIV_ROUND_CLOSEST(req->best_parent_rate, rate);
if (a < SI5351_MULTISYNTH_A_MIN)
a = SI5351_MULTISYNTH_A_MIN;
if (a > SI5351_MULTISYNTH67_A_MAX)
@ -700,7 +703,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
}
/* determine integer part of divider equation */
a = *parent_rate / rate;
a = req->best_parent_rate / rate;
if (a < SI5351_MULTISYNTH_A_MIN)
a = SI5351_MULTISYNTH_A_MIN;
if (a > SI5351_MULTISYNTH_A_MAX)
@ -708,7 +711,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
/* find best approximation for b/c = fVCO mod fOUT */
denom = 1000 * 1000;
lltmp = (*parent_rate) % rate;
lltmp = req->best_parent_rate % rate;
lltmp *= denom;
do_div(lltmp, rate);
rfrac = (unsigned long)lltmp;
@ -722,7 +725,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
}
/* recalculate rate by fOUT = fIN / (a + b/c) */
lltmp = *parent_rate;
lltmp = req->best_parent_rate;
lltmp *= c;
do_div(lltmp, a * c + b);
rate = (unsigned long)lltmp;
@ -747,9 +750,11 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
dev_dbg(&hwdata->drvdata->client->dev,
"%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n",
__func__, clk_hw_get_name(hw), a, b, c, divby4,
*parent_rate, rate);
req->best_parent_rate, rate);
return rate;
req->rate = rate;
return 0;
}
static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate,
@ -789,7 +794,7 @@ static const struct clk_ops si5351_msynth_ops = {
.set_parent = si5351_msynth_set_parent,
.get_parent = si5351_msynth_get_parent,
.recalc_rate = si5351_msynth_recalc_rate,
.round_rate = si5351_msynth_round_rate,
.determine_rate = si5351_msynth_determine_rate,
.set_rate = si5351_msynth_set_rate,
};
@ -1032,11 +1037,12 @@ static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw,
return parent_rate >> rdiv;
}
static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int si5351_clkout_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct si5351_hw_data *hwdata =
container_of(hw, struct si5351_hw_data, hw);
unsigned long rate = req->rate;
unsigned char rdiv;
/* clkout6/7 can only handle output freqencies < 150MHz */
@ -1058,13 +1064,13 @@ static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
rdiv += 1;
rate *= 2;
}
*parent_rate = rate;
req->best_parent_rate = rate;
} else {
unsigned long new_rate, new_err, err;
/* round to closed rdiv */
rdiv = SI5351_OUTPUT_CLK_DIV_1;
new_rate = *parent_rate;
new_rate = req->best_parent_rate;
err = abs(new_rate - rate);
do {
new_rate >>= 1;
@ -1075,14 +1081,15 @@ static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
err = new_err;
} while (1);
}
rate = *parent_rate >> rdiv;
rate = req->best_parent_rate >> rdiv;
dev_dbg(&hwdata->drvdata->client->dev,
"%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n",
__func__, clk_hw_get_name(hw), (1 << rdiv),
*parent_rate, rate);
req->best_parent_rate, rate);
return rate;
req->rate = rate;
return 0;
}
static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@ -1142,7 +1149,7 @@ static const struct clk_ops si5351_clkout_ops = {
.set_parent = si5351_clkout_set_parent,
.get_parent = si5351_clkout_get_parent,
.recalc_rate = si5351_clkout_recalc_rate,
.round_rate = si5351_clkout_round_rate,
.determine_rate = si5351_clkout_determine_rate,
.set_rate = si5351_clkout_set_rate,
};

View file

@ -1045,6 +1045,7 @@ static int cclk_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops cclk_mux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = cclk_mux_get_parent,
.set_parent = cclk_mux_set_parent,
};

View file

@ -282,6 +282,7 @@ static int vc5_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops vc5_mux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = vc5_mux_set_parent,
.get_parent = vc5_mux_get_parent,
};
@ -726,6 +727,7 @@ static int vc5_clk_out_set_parent(struct clk_hw *hw, u8 index)
static const struct clk_ops vc5_clk_out_ops = {
.prepare = vc5_clk_out_prepare,
.unprepare = vc5_clk_out_unprepare,
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = vc5_clk_out_set_parent,
.get_parent = vc5_clk_out_get_parent,
};

View file

@ -329,6 +329,7 @@ static const struct clk_ops wm831x_clkout_ops = {
.is_prepared = wm831x_clkout_is_prepared,
.prepare = wm831x_clkout_prepare,
.unprepare = wm831x_clkout_unprepare,
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = wm831x_clkout_get_parent,
.set_parent = wm831x_clkout_set_parent,
};

View file

@ -594,6 +594,47 @@ clk_core_forward_rate_req(struct clk_core *core,
req->max_rate = old_req->max_rate;
}
static int
clk_core_determine_rate_no_reparent(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_core *core = hw->core;
struct clk_core *parent = core->parent;
unsigned long best;
int ret;
if (core->flags & CLK_SET_RATE_PARENT) {
struct clk_rate_request parent_req;
if (!parent) {
req->rate = 0;
return 0;
}
clk_core_forward_rate_req(core, req, parent, &parent_req,
req->rate);
trace_clk_rate_request_start(&parent_req);
ret = clk_core_round_rate_nolock(parent, &parent_req);
if (ret)
return ret;
trace_clk_rate_request_done(&parent_req);
best = parent_req.rate;
} else if (parent) {
best = clk_core_get_rate_nolock(parent);
} else {
best = clk_core_get_rate_nolock(core);
}
req->best_parent_rate = best;
req->rate = best;
return 0;
}
int clk_mux_determine_rate_flags(struct clk_hw *hw,
struct clk_rate_request *req,
unsigned long flags)
@ -603,35 +644,8 @@ int clk_mux_determine_rate_flags(struct clk_hw *hw,
unsigned long best = 0;
/* if NO_REPARENT flag set, pass through to current parent */
if (core->flags & CLK_SET_RATE_NO_REPARENT) {
parent = core->parent;
if (core->flags & CLK_SET_RATE_PARENT) {
struct clk_rate_request parent_req;
if (!parent) {
req->rate = 0;
return 0;
}
clk_core_forward_rate_req(core, req, parent, &parent_req, req->rate);
trace_clk_rate_request_start(&parent_req);
ret = clk_core_round_rate_nolock(parent, &parent_req);
if (ret)
return ret;
trace_clk_rate_request_done(&parent_req);
best = parent_req.rate;
} else if (parent) {
best = clk_core_get_rate_nolock(parent);
} else {
best = clk_core_get_rate_nolock(core);
}
goto out;
}
if (core->flags & CLK_SET_RATE_NO_REPARENT)
return clk_core_determine_rate_no_reparent(hw, req);
/* find the parent that can provide the fastest rate <= rate */
num_parents = core->num_parents;
@ -670,9 +684,7 @@ int clk_mux_determine_rate_flags(struct clk_hw *hw,
if (!best_parent)
return -EINVAL;
out:
if (best_parent)
req->best_parent_hw = best_parent->hw;
req->best_parent_hw = best_parent->hw;
req->best_parent_rate = best;
req->rate = best;
@ -772,6 +784,25 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw,
}
EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
/*
* clk_hw_determine_rate_no_reparent - clk_ops::determine_rate implementation for a clk that doesn't reparent
* @hw: mux type clk to determine rate on
* @req: rate request, also used to return preferred frequency
*
* Helper for finding best parent rate to provide a given frequency.
* This can be used directly as a determine_rate callback (e.g. for a
* mux), or from a more complex clock that may combine a mux with other
* operations.
*
* Returns: 0 on success, -EERROR value on error
*/
int clk_hw_determine_rate_no_reparent(struct clk_hw *hw,
struct clk_rate_request *req)
{
return clk_core_determine_rate_no_reparent(hw, req);
}
EXPORT_SYMBOL_GPL(clk_hw_determine_rate_no_reparent);
/*** clk api ***/
static void clk_core_rate_unprotect(struct clk_core *core)
@ -1549,6 +1580,7 @@ void clk_hw_forward_rate_request(const struct clk_hw *hw,
parent->core, req,
parent_rate);
}
EXPORT_SYMBOL_GPL(clk_hw_forward_rate_request);
static bool clk_core_can_round(struct clk_core * const core)
{
@ -3745,6 +3777,13 @@ static int __clk_core_init(struct clk_core *core)
goto out;
}
if (core->ops->set_parent && !core->ops->determine_rate) {
pr_err("%s: %s must implement .set_parent & .determine_rate\n",
__func__, core->name);
ret = -EINVAL;
goto out;
}
if (core->num_parents > 1 && !core->ops->get_parent) {
pr_err("%s: %s must implement .get_parent as it has multi parents\n",
__func__, core->name);
@ -4301,11 +4340,18 @@ static int clk_nodrv_set_parent(struct clk_hw *hw, u8 index)
return -ENXIO;
}
static int clk_nodrv_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
return -ENXIO;
}
static const struct clk_ops clk_nodrv_ops = {
.enable = clk_nodrv_prepare_enable,
.disable = clk_nodrv_disable_unprepare,
.prepare = clk_nodrv_prepare_enable,
.unprepare = clk_nodrv_disable_unprepare,
.determine_rate = clk_nodrv_determine_rate,
.set_rate = clk_nodrv_set_rate,
.set_parent = clk_nodrv_set_parent,
};

View file

@ -104,6 +104,23 @@ static const struct clk_ops clk_dummy_minimize_rate_ops = {
};
static const struct clk_ops clk_dummy_single_parent_ops = {
/*
* FIXME: Even though we should probably be able to use
* __clk_mux_determine_rate() here, if we use it and call
* clk_round_rate() or clk_set_rate() with a rate lower than
* what all the parents can provide, it will return -EINVAL.
*
* This is due to the fact that it has the undocumented
* behaviour to always pick up the closest rate higher than the
* requested rate. If we get something lower, it thus considers
* that it's not acceptable and will return an error.
*
* It's somewhat inconsistent and creates a weird threshold
* between rates above the parent rate which would be rounded to
* what the parent can provide, but rates below will simply
* return an error.
*/
.determine_rate = __clk_mux_determine_rate_closest,
.set_parent = clk_dummy_single_set_parent,
.get_parent = clk_dummy_single_get_parent,
};
@ -141,6 +158,12 @@ static const struct clk_ops clk_multiple_parents_mux_ops = {
.determine_rate = __clk_mux_determine_rate_closest,
};
static const struct clk_ops clk_multiple_parents_no_reparent_mux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_multiple_parents_mux_get_parent,
.set_parent = clk_multiple_parents_mux_set_parent,
};
static int clk_test_init_with_ops(struct kunit *test, const struct clk_ops *ops)
{
struct clk_dummy_context *ctx;
@ -266,7 +289,8 @@ static void clk_test_round_set_get_rate(struct kunit *test)
struct clk_dummy_context *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rounded_rate, set_rate;
unsigned long set_rate;
long rounded_rate;
rounded_rate = clk_round_rate(clk, DUMMY_CLOCK_RATE_1);
KUNIT_ASSERT_GT(test, rounded_rate, 0);
@ -851,7 +875,7 @@ clk_test_orphan_transparent_multiple_parent_mux_set_range_round_rate(struct kuni
struct clk_multiple_parent_ctx *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
struct clk *clk = clk_hw_get_clk(hw, NULL);
unsigned long rate;
long rate;
int ret;
ret = clk_set_rate_range(clk, DUMMY_CLOCK_RATE_1, DUMMY_CLOCK_RATE_2);
@ -1090,7 +1114,7 @@ clk_test_single_parent_mux_set_range_round_rate_parent_only(struct kunit *test)
struct clk_hw *hw = &ctx->hw;
struct clk *clk = clk_hw_get_clk(hw, NULL);
struct clk *parent;
unsigned long rate;
long rate;
int ret;
parent = clk_get_parent(clk);
@ -1120,7 +1144,7 @@ clk_test_single_parent_mux_set_range_round_rate_child_smaller(struct kunit *test
struct clk_hw *hw = &ctx->hw;
struct clk *clk = clk_hw_get_clk(hw, NULL);
struct clk *parent;
unsigned long rate;
long rate;
int ret;
parent = clk_get_parent(clk);
@ -1158,7 +1182,7 @@ clk_test_single_parent_mux_set_range_round_rate_parent_smaller(struct kunit *tes
struct clk_hw *hw = &ctx->hw;
struct clk *clk = clk_hw_get_clk(hw, NULL);
struct clk *parent;
unsigned long rate;
long rate;
int ret;
parent = clk_get_parent(clk);
@ -2394,10 +2418,156 @@ static struct kunit_suite clk_mux_notifier_test_suite = {
.test_cases = clk_mux_notifier_test_cases,
};
static int
clk_mux_no_reparent_test_init(struct kunit *test)
{
struct clk_multiple_parent_ctx *ctx;
const char *parents[2] = { "parent-0", "parent-1"};
int ret;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
test->priv = ctx;
ctx->parents_ctx[0].hw.init = CLK_HW_INIT_NO_PARENT("parent-0",
&clk_dummy_rate_ops,
0);
ctx->parents_ctx[0].rate = DUMMY_CLOCK_RATE_1;
ret = clk_hw_register(NULL, &ctx->parents_ctx[0].hw);
if (ret)
return ret;
ctx->parents_ctx[1].hw.init = CLK_HW_INIT_NO_PARENT("parent-1",
&clk_dummy_rate_ops,
0);
ctx->parents_ctx[1].rate = DUMMY_CLOCK_RATE_2;
ret = clk_hw_register(NULL, &ctx->parents_ctx[1].hw);
if (ret)
return ret;
ctx->current_parent = 0;
ctx->hw.init = CLK_HW_INIT_PARENTS("test-mux", parents,
&clk_multiple_parents_no_reparent_mux_ops,
0);
ret = clk_hw_register(NULL, &ctx->hw);
if (ret)
return ret;
return 0;
}
static void
clk_mux_no_reparent_test_exit(struct kunit *test)
{
struct clk_multiple_parent_ctx *ctx = test->priv;
clk_hw_unregister(&ctx->hw);
clk_hw_unregister(&ctx->parents_ctx[0].hw);
clk_hw_unregister(&ctx->parents_ctx[1].hw);
}
/*
* Test that if the we have a mux that cannot change parent and we call
* clk_round_rate() on it with a rate that should cause it to change
* parent, it won't.
*/
static void clk_mux_no_reparent_round_rate(struct kunit *test)
{
struct clk_multiple_parent_ctx *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
struct clk *clk = clk_hw_get_clk(hw, NULL);
struct clk *other_parent, *parent;
unsigned long other_parent_rate;
unsigned long parent_rate;
long rounded_rate;
parent = clk_get_parent(clk);
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
parent_rate = clk_get_rate(parent);
KUNIT_ASSERT_GT(test, parent_rate, 0);
other_parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_parent);
KUNIT_ASSERT_FALSE(test, clk_is_match(parent, other_parent));
other_parent_rate = clk_get_rate(other_parent);
KUNIT_ASSERT_GT(test, other_parent_rate, 0);
clk_put(other_parent);
rounded_rate = clk_round_rate(clk, other_parent_rate);
KUNIT_ASSERT_GT(test, rounded_rate, 0);
KUNIT_EXPECT_EQ(test, rounded_rate, parent_rate);
clk_put(clk);
}
/*
* Test that if the we have a mux that cannot change parent and we call
* clk_set_rate() on it with a rate that should cause it to change
* parent, it won't.
*/
static void clk_mux_no_reparent_set_rate(struct kunit *test)
{
struct clk_multiple_parent_ctx *ctx = test->priv;
struct clk_hw *hw = &ctx->hw;
struct clk *clk = clk_hw_get_clk(hw, NULL);
struct clk *other_parent, *parent;
unsigned long other_parent_rate;
unsigned long parent_rate;
unsigned long rate;
int ret;
parent = clk_get_parent(clk);
KUNIT_ASSERT_PTR_NE(test, parent, NULL);
parent_rate = clk_get_rate(parent);
KUNIT_ASSERT_GT(test, parent_rate, 0);
other_parent = clk_hw_get_clk(&ctx->parents_ctx[1].hw, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other_parent);
KUNIT_ASSERT_FALSE(test, clk_is_match(parent, other_parent));
other_parent_rate = clk_get_rate(other_parent);
KUNIT_ASSERT_GT(test, other_parent_rate, 0);
clk_put(other_parent);
ret = clk_set_rate(clk, other_parent_rate);
KUNIT_ASSERT_EQ(test, ret, 0);
rate = clk_get_rate(clk);
KUNIT_ASSERT_GT(test, rate, 0);
KUNIT_EXPECT_EQ(test, rate, parent_rate);
clk_put(clk);
}
static struct kunit_case clk_mux_no_reparent_test_cases[] = {
KUNIT_CASE(clk_mux_no_reparent_round_rate),
KUNIT_CASE(clk_mux_no_reparent_set_rate),
{}
};
/*
* Test suite for a clock mux that isn't allowed to change parent, using
* the clk_hw_determine_rate_no_reparent() helper.
*
* These tests exercise that helper, and the proper selection of
* rates and parents.
*/
static struct kunit_suite clk_mux_no_reparent_test_suite = {
.name = "clk-mux-no-reparent",
.init = clk_mux_no_reparent_test_init,
.exit = clk_mux_no_reparent_test_exit,
.test_cases = clk_mux_no_reparent_test_cases,
};
kunit_test_suites(
&clk_leaf_mux_set_rate_parent_test_suite,
&clk_test_suite,
&clk_multiple_parents_mux_test_suite,
&clk_mux_no_reparent_test_suite,
&clk_mux_notifier_test_suite,
&clk_orphan_transparent_multiple_parent_mux_test_suite,
&clk_orphan_transparent_single_parent_test_suite,

View file

@ -229,6 +229,7 @@ static u8 da8xx_cfgchip_mux_clk_get_parent(struct clk_hw *hw)
}
static const struct clk_ops da8xx_cfgchip_mux_clk_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = da8xx_cfgchip_mux_clk_set_parent,
.get_parent = da8xx_cfgchip_mux_clk_get_parent,
};
@ -461,10 +462,12 @@ static unsigned long da8xx_usb0_clk48_recalc_rate(struct clk_hw *hw,
return 48000000;
}
static long da8xx_usb0_clk48_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int da8xx_usb0_clk48_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
return 48000000;
req->rate = 48000000;
return 0;
}
static int da8xx_usb0_clk48_set_parent(struct clk_hw *hw, u8 index)
@ -493,7 +496,7 @@ static const struct clk_ops da8xx_usb0_clk48_ops = {
.disable = da8xx_usb0_clk48_disable,
.is_enabled = da8xx_usb0_clk48_is_enabled,
.recalc_rate = da8xx_usb0_clk48_recalc_rate,
.round_rate = da8xx_usb0_clk48_round_rate,
.determine_rate = da8xx_usb0_clk48_determine_rate,
.set_parent = da8xx_usb0_clk48_set_parent,
.get_parent = da8xx_usb0_clk48_get_parent,
};
@ -564,6 +567,7 @@ static u8 da8xx_usb1_clk48_get_parent(struct clk_hw *hw)
}
static const struct clk_ops da8xx_usb1_clk48_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = da8xx_usb1_clk48_set_parent,
.get_parent = da8xx_usb1_clk48_get_parent,
};

View file

@ -148,6 +148,7 @@ static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_busy_mux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_busy_mux_get_parent,
.set_parent = clk_busy_mux_set_parent,
};

View file

@ -60,6 +60,7 @@ static int clk_fixup_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_fixup_mux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_fixup_mux_get_parent,
.set_parent = clk_fixup_mux_set_parent,
};

View file

@ -250,6 +250,23 @@ static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
return le32_to_cpu(msg.data.resp.rate);
}
/*
* clk_scu_determine_rate - Returns the closest rate for a SCU clock
* @hw: clock to round rate for
* @req: clock rate request
*
* Returns 0 on success, a negative error on failure
*/
static int clk_scu_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
/*
* Assume we support all the requested rate and let the SCU firmware
* to handle the left work
*/
return 0;
}
/*
* clk_scu_round_rate - Round clock rate for a SCU clock
* @hw: clock to round rate for
@ -425,7 +442,7 @@ static void clk_scu_unprepare(struct clk_hw *hw)
static const struct clk_ops clk_scu_ops = {
.recalc_rate = clk_scu_recalc_rate,
.round_rate = clk_scu_round_rate,
.determine_rate = clk_scu_determine_rate,
.set_rate = clk_scu_set_rate,
.get_parent = clk_scu_get_parent,
.set_parent = clk_scu_set_parent,
@ -785,6 +802,7 @@ static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_gpr_mux_scu_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_gpr_mux_scu_get_parent,
.set_parent = clk_gpr_mux_scu_set_parent,
};

View file

@ -491,22 +491,23 @@ ingenic_clk_calc_div(struct clk_hw *hw,
return div;
}
static long
ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long *parent_rate)
static int ingenic_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
unsigned int div = 1;
if (clk_info->type & CGU_CLK_DIV)
div = ingenic_clk_calc_div(hw, clk_info, *parent_rate, req_rate);
div = ingenic_clk_calc_div(hw, clk_info, req->best_parent_rate,
req->rate);
else if (clk_info->type & CGU_CLK_FIXDIV)
div = clk_info->fixdiv.div;
else if (clk_hw_can_set_rate_parent(hw))
*parent_rate = req_rate;
req->best_parent_rate = req->rate;
return DIV_ROUND_UP(*parent_rate, div);
req->rate = DIV_ROUND_UP(req->best_parent_rate, div);
return 0;
}
static inline int ingenic_clk_check_stable(struct ingenic_cgu *cgu,
@ -626,7 +627,7 @@ static const struct clk_ops ingenic_clk_ops = {
.set_parent = ingenic_clk_set_parent,
.recalc_rate = ingenic_clk_recalc_rate,
.round_rate = ingenic_clk_round_rate,
.determine_rate = ingenic_clk_determine_rate,
.set_rate = ingenic_clk_set_rate,
.enable = ingenic_clk_enable,

View file

@ -178,18 +178,21 @@ static u8 ingenic_tcu_get_prescale(unsigned long rate, unsigned long req_rate)
return 5; /* /1024 divider */
}
static long ingenic_tcu_round_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long *parent_rate)
static int ingenic_tcu_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
unsigned long rate = *parent_rate;
unsigned long rate = req->best_parent_rate;
u8 prescale;
if (req_rate > rate)
return rate;
if (req->rate > rate) {
req->rate = rate;
return 0;
}
prescale = ingenic_tcu_get_prescale(rate, req_rate);
prescale = ingenic_tcu_get_prescale(rate, req->rate);
return rate >> (prescale * 2);
req->rate = rate >> (prescale * 2);
return 0;
}
static int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate,
@ -219,7 +222,7 @@ static const struct clk_ops ingenic_tcu_clk_ops = {
.set_parent = ingenic_tcu_set_parent,
.recalc_rate = ingenic_tcu_recalc_rate,
.round_rate = ingenic_tcu_round_rate,
.determine_rate = ingenic_tcu_determine_rate,
.set_rate = ingenic_tcu_set_rate,
.enable = ingenic_tcu_enable,

View file

@ -53,6 +53,7 @@ static int clk_cpumux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_cpumux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_cpumux_get_parent,
.set_parent = clk_cpumux_set_parent,
};

View file

@ -99,6 +99,26 @@ config COMMON_CLK_AXG_AUDIO
Support for the audio clock controller on AmLogic A113D devices,
aka axg, Say Y if you want audio subsystem to work.
config COMMON_CLK_A1_PLL
tristate "Amlogic A1 SoC PLL controller support"
depends on ARM64
select COMMON_CLK_MESON_REGMAP
select COMMON_CLK_MESON_PLL
help
Support for the PLL clock controller on Amlogic A113L based
device, A1 SoC Family. Say Y if you want A1 PLL clock controller
to work.
config COMMON_CLK_A1_PERIPHERALS
tristate "Amlogic A1 SoC Peripherals clock controller support"
depends on ARM64
select COMMON_CLK_MESON_DUALDIV
select COMMON_CLK_MESON_REGMAP
help
Support for the Peripherals clock controller on Amlogic A113L based
device, A1 SoC Family. Say Y if you want A1 Peripherals clock
controller to work.
config COMMON_CLK_G12A
tristate "G12 and SM1 SoC clock controllers support"
depends on ARM64

View file

@ -16,6 +16,8 @@ obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,113 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Amlogic A1 Peripherals Clock Controller internals
*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
* Author: Jian Hu <jian.hu@amlogic.com>
*
* Copyright (c) 2023, SberDevices. All Rights Reserved.
* Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
*/
#ifndef __A1_PERIPHERALS_H
#define __A1_PERIPHERALS_H
/* peripherals clock controller register offset */
#define SYS_OSCIN_CTRL 0x0
#define RTC_BY_OSCIN_CTRL0 0x4
#define RTC_BY_OSCIN_CTRL1 0x8
#define RTC_CTRL 0xc
#define SYS_CLK_CTRL0 0x10
#define SYS_CLK_EN0 0x1c
#define SYS_CLK_EN1 0x20
#define AXI_CLK_EN 0x24
#define DSPA_CLK_EN 0x28
#define DSPB_CLK_EN 0x2c
#define DSPA_CLK_CTRL0 0x30
#define DSPB_CLK_CTRL0 0x34
#define CLK12_24_CTRL 0x38
#define GEN_CLK_CTRL 0x3c
#define SAR_ADC_CLK_CTRL 0xc0
#define PWM_CLK_AB_CTRL 0xc4
#define PWM_CLK_CD_CTRL 0xc8
#define PWM_CLK_EF_CTRL 0xcc
#define SPICC_CLK_CTRL 0xd0
#define TS_CLK_CTRL 0xd4
#define SPIFC_CLK_CTRL 0xd8
#define USB_BUSCLK_CTRL 0xdc
#define SD_EMMC_CLK_CTRL 0xe0
#define CECA_CLK_CTRL0 0xe4
#define CECA_CLK_CTRL1 0xe8
#define CECB_CLK_CTRL0 0xec
#define CECB_CLK_CTRL1 0xf0
#define PSRAM_CLK_CTRL 0xf4
#define DMC_CLK_CTRL 0xf8
/* include the CLKIDs that have been made part of the DT binding */
#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h>
/*
* CLKID index values for internal clocks
*
* These indices are entirely contrived and do not map onto the hardware.
* It has now been decided to expose everything by default in the DT header:
* include/dt-bindings/clock/a1-peripherals-clkc.h.
* Only the clocks ids we don't want to expose, such as the internal muxes and
* dividers of composite clocks, will remain defined here.
*/
#define CLKID_XTAL_IN 0
#define CLKID_DSPA_SEL 61
#define CLKID_DSPB_SEL 62
#define CLKID_SARADC_SEL 74
#define CLKID_SYS_A_SEL 89
#define CLKID_SYS_A_DIV 90
#define CLKID_SYS_A 91
#define CLKID_SYS_B_SEL 92
#define CLKID_SYS_B_DIV 93
#define CLKID_SYS_B 94
#define CLKID_DSPA_A_DIV 96
#define CLKID_DSPA_A 97
#define CLKID_DSPA_B_DIV 99
#define CLKID_DSPA_B 100
#define CLKID_DSPB_A_DIV 102
#define CLKID_DSPB_A 103
#define CLKID_DSPB_B_DIV 105
#define CLKID_DSPB_B 106
#define CLKID_RTC_32K_IN 107
#define CLKID_RTC_32K_DIV 108
#define CLKID_RTC_32K_XTAL 109
#define CLKID_RTC_32K_SEL 110
#define CLKID_CECB_32K_IN 111
#define CLKID_CECB_32K_DIV 112
#define CLKID_CECA_32K_IN 115
#define CLKID_CECA_32K_DIV 116
#define CLKID_DIV2_PRE 119
#define CLKID_24M_DIV2 120
#define CLKID_GEN_DIV 122
#define CLKID_SARADC_DIV 123
#define CLKID_PWM_A_DIV 125
#define CLKID_PWM_B_DIV 127
#define CLKID_PWM_C_DIV 129
#define CLKID_PWM_D_DIV 131
#define CLKID_PWM_E_DIV 133
#define CLKID_PWM_F_DIV 135
#define CLKID_SPICC_SEL 136
#define CLKID_SPICC_DIV 137
#define CLKID_SPICC_SEL2 138
#define CLKID_TS_DIV 139
#define CLKID_SPIFC_SEL 140
#define CLKID_SPIFC_DIV 141
#define CLKID_SPIFC_SEL2 142
#define CLKID_USB_BUS_SEL 143
#define CLKID_USB_BUS_DIV 144
#define CLKID_SD_EMMC_SEL 145
#define CLKID_SD_EMMC_DIV 146
#define CLKID_PSRAM_SEL 148
#define CLKID_PSRAM_DIV 149
#define CLKID_PSRAM_SEL2 150
#define CLKID_DMC_SEL 151
#define CLKID_DMC_DIV 152
#define CLKID_DMC_SEL2 153
#define NR_CLKS 154
#endif /* __A1_PERIPHERALS_H */

356
drivers/clk/meson/a1-pll.c Normal file
View file

@ -0,0 +1,356 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
* Author: Jian Hu <jian.hu@amlogic.com>
*
* Copyright (c) 2023, SberDevices. All Rights Reserved.
* Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
*/
#include <linux/clk-provider.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include "a1-pll.h"
#include "clk-regmap.h"
static struct clk_regmap fixed_pll_dco = {
.data = &(struct meson_clk_pll_data){
.en = {
.reg_off = ANACTRL_FIXPLL_CTRL0,
.shift = 28,
.width = 1,
},
.m = {
.reg_off = ANACTRL_FIXPLL_CTRL0,
.shift = 0,
.width = 8,
},
.n = {
.reg_off = ANACTRL_FIXPLL_CTRL0,
.shift = 10,
.width = 5,
},
.frac = {
.reg_off = ANACTRL_FIXPLL_CTRL1,
.shift = 0,
.width = 19,
},
.l = {
.reg_off = ANACTRL_FIXPLL_STS,
.shift = 31,
.width = 1,
},
.rst = {
.reg_off = ANACTRL_FIXPLL_CTRL0,
.shift = 29,
.width = 1,
},
},
.hw.init = &(struct clk_init_data){
.name = "fixed_pll_dco",
.ops = &meson_clk_pll_ro_ops,
.parent_data = &(const struct clk_parent_data) {
.fw_name = "fixpll_in",
},
.num_parents = 1,
},
};
static struct clk_regmap fixed_pll = {
.data = &(struct clk_regmap_gate_data){
.offset = ANACTRL_FIXPLL_CTRL0,
.bit_idx = 20,
},
.hw.init = &(struct clk_init_data) {
.name = "fixed_pll",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&fixed_pll_dco.hw
},
.num_parents = 1,
},
};
static const struct pll_mult_range hifi_pll_mult_range = {
.min = 32,
.max = 64,
};
static const struct reg_sequence hifi_init_regs[] = {
{ .reg = ANACTRL_HIFIPLL_CTRL1, .def = 0x01800000 },
{ .reg = ANACTRL_HIFIPLL_CTRL2, .def = 0x00001100 },
{ .reg = ANACTRL_HIFIPLL_CTRL3, .def = 0x100a1100 },
{ .reg = ANACTRL_HIFIPLL_CTRL4, .def = 0x00302000 },
{ .reg = ANACTRL_HIFIPLL_CTRL0, .def = 0x01f18000 },
};
static struct clk_regmap hifi_pll = {
.data = &(struct meson_clk_pll_data){
.en = {
.reg_off = ANACTRL_HIFIPLL_CTRL0,
.shift = 28,
.width = 1,
},
.m = {
.reg_off = ANACTRL_HIFIPLL_CTRL0,
.shift = 0,
.width = 8,
},
.n = {
.reg_off = ANACTRL_HIFIPLL_CTRL0,
.shift = 10,
.width = 5,
},
.frac = {
.reg_off = ANACTRL_HIFIPLL_CTRL1,
.shift = 0,
.width = 19,
},
.l = {
.reg_off = ANACTRL_HIFIPLL_STS,
.shift = 31,
.width = 1,
},
.current_en = {
.reg_off = ANACTRL_HIFIPLL_CTRL0,
.shift = 26,
.width = 1,
},
.l_detect = {
.reg_off = ANACTRL_HIFIPLL_CTRL2,
.shift = 6,
.width = 1,
},
.range = &hifi_pll_mult_range,
.init_regs = hifi_init_regs,
.init_count = ARRAY_SIZE(hifi_init_regs),
},
.hw.init = &(struct clk_init_data){
.name = "hifi_pll",
.ops = &meson_clk_pll_ops,
.parent_data = &(const struct clk_parent_data) {
.fw_name = "hifipll_in",
},
.num_parents = 1,
},
};
static struct clk_fixed_factor fclk_div2_div = {
.mult = 1,
.div = 2,
.hw.init = &(struct clk_init_data){
.name = "fclk_div2_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
&fixed_pll.hw
},
.num_parents = 1,
},
};
static struct clk_regmap fclk_div2 = {
.data = &(struct clk_regmap_gate_data){
.offset = ANACTRL_FIXPLL_CTRL0,
.bit_idx = 21,
},
.hw.init = &(struct clk_init_data){
.name = "fclk_div2",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&fclk_div2_div.hw
},
.num_parents = 1,
/*
* This clock is used by DDR clock in BL2 firmware
* and is required by the platform to operate correctly.
* Until the following condition are met, we need this clock to
* be marked as critical:
* a) Mark the clock used by a firmware resource, if possible
* b) CCF has a clock hand-off mechanism to make the sure the
* clock stays on until the proper driver comes along
*/
.flags = CLK_IS_CRITICAL,
},
};
static struct clk_fixed_factor fclk_div3_div = {
.mult = 1,
.div = 3,
.hw.init = &(struct clk_init_data){
.name = "fclk_div3_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
&fixed_pll.hw
},
.num_parents = 1,
},
};
static struct clk_regmap fclk_div3 = {
.data = &(struct clk_regmap_gate_data){
.offset = ANACTRL_FIXPLL_CTRL0,
.bit_idx = 22,
},
.hw.init = &(struct clk_init_data){
.name = "fclk_div3",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&fclk_div3_div.hw
},
.num_parents = 1,
/*
* This clock is used by APB bus which is set in boot ROM code
* and is required by the platform to operate correctly.
*/
.flags = CLK_IS_CRITICAL,
},
};
static struct clk_fixed_factor fclk_div5_div = {
.mult = 1,
.div = 5,
.hw.init = &(struct clk_init_data){
.name = "fclk_div5_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
&fixed_pll.hw
},
.num_parents = 1,
},
};
static struct clk_regmap fclk_div5 = {
.data = &(struct clk_regmap_gate_data){
.offset = ANACTRL_FIXPLL_CTRL0,
.bit_idx = 23,
},
.hw.init = &(struct clk_init_data){
.name = "fclk_div5",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&fclk_div5_div.hw
},
.num_parents = 1,
/*
* This clock is used by AXI bus which setted in Romcode
* and is required by the platform to operate correctly.
*/
.flags = CLK_IS_CRITICAL,
},
};
static struct clk_fixed_factor fclk_div7_div = {
.mult = 1,
.div = 7,
.hw.init = &(struct clk_init_data){
.name = "fclk_div7_div",
.ops = &clk_fixed_factor_ops,
.parent_hws = (const struct clk_hw *[]) {
&fixed_pll.hw
},
.num_parents = 1,
},
};
static struct clk_regmap fclk_div7 = {
.data = &(struct clk_regmap_gate_data){
.offset = ANACTRL_FIXPLL_CTRL0,
.bit_idx = 24,
},
.hw.init = &(struct clk_init_data){
.name = "fclk_div7",
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) {
&fclk_div7_div.hw
},
.num_parents = 1,
},
};
/* Array of all clocks registered by this provider */
static struct clk_hw_onecell_data a1_pll_clks = {
.hws = {
[CLKID_FIXED_PLL_DCO] = &fixed_pll_dco.hw,
[CLKID_FIXED_PLL] = &fixed_pll.hw,
[CLKID_FCLK_DIV2_DIV] = &fclk_div2_div.hw,
[CLKID_FCLK_DIV3_DIV] = &fclk_div3_div.hw,
[CLKID_FCLK_DIV5_DIV] = &fclk_div5_div.hw,
[CLKID_FCLK_DIV7_DIV] = &fclk_div7_div.hw,
[CLKID_FCLK_DIV2] = &fclk_div2.hw,
[CLKID_FCLK_DIV3] = &fclk_div3.hw,
[CLKID_FCLK_DIV5] = &fclk_div5.hw,
[CLKID_FCLK_DIV7] = &fclk_div7.hw,
[CLKID_HIFI_PLL] = &hifi_pll.hw,
[NR_PLL_CLKS] = NULL,
},
.num = NR_PLL_CLKS,
};
static struct clk_regmap *const a1_pll_regmaps[] = {
&fixed_pll_dco,
&fixed_pll,
&fclk_div2,
&fclk_div3,
&fclk_div5,
&fclk_div7,
&hifi_pll,
};
static struct regmap_config a1_pll_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int meson_a1_pll_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
void __iomem *base;
struct regmap *map;
int clkid, i, err;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return dev_err_probe(dev, PTR_ERR(base),
"can't ioremap resource\n");
map = devm_regmap_init_mmio(dev, base, &a1_pll_regmap_cfg);
if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map),
"can't init regmap mmio region\n");
/* Populate regmap for the regmap backed clocks */
for (i = 0; i < ARRAY_SIZE(a1_pll_regmaps); i++)
a1_pll_regmaps[i]->map = map;
/* Register clocks */
for (clkid = 0; clkid < a1_pll_clks.num; clkid++) {
err = devm_clk_hw_register(dev, a1_pll_clks.hws[clkid]);
if (err)
return dev_err_probe(dev, err,
"clock[%d] registration failed\n",
clkid);
}
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
&a1_pll_clks);
}
static const struct of_device_id a1_pll_clkc_match_table[] = {
{ .compatible = "amlogic,a1-pll-clkc", },
{}
};
MODULE_DEVICE_TABLE(of, a1_pll_clkc_match_table);
static struct platform_driver a1_pll_clkc_driver = {
.probe = meson_a1_pll_probe,
.driver = {
.name = "a1-pll-clkc",
.of_match_table = a1_pll_clkc_match_table,
},
};
module_platform_driver(a1_pll_clkc_driver);
MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Amlogic A1 PLL Clock Controller internals
*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
* Author: Jian Hu <jian.hu@amlogic.com>
*
* Copyright (c) 2023, SberDevices. All Rights Reserved.
* Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
*/
#ifndef __A1_PLL_H
#define __A1_PLL_H
#include "clk-pll.h"
/* PLL register offset */
#define ANACTRL_FIXPLL_CTRL0 0x0
#define ANACTRL_FIXPLL_CTRL1 0x4
#define ANACTRL_FIXPLL_STS 0x14
#define ANACTRL_HIFIPLL_CTRL0 0xc0
#define ANACTRL_HIFIPLL_CTRL1 0xc4
#define ANACTRL_HIFIPLL_CTRL2 0xc8
#define ANACTRL_HIFIPLL_CTRL3 0xcc
#define ANACTRL_HIFIPLL_CTRL4 0xd0
#define ANACTRL_HIFIPLL_STS 0xd4
/* include the CLKIDs that have been made part of the DT binding */
#include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
/*
* CLKID index values for internal clocks
*
* These indices are entirely contrived and do not map onto the hardware.
* It has now been decided to expose everything by default in the DT header:
* include/dt-bindings/clock/a1-pll-clkc.h. Only the clocks ids we don't want
* to expose, such as the internal muxes and dividers of composite clocks,
* will remain defined here.
*/
#define CLKID_FIXED_PLL_DCO 0
#define CLKID_FCLK_DIV2_DIV 2
#define CLKID_FCLK_DIV3_DIV 3
#define CLKID_FCLK_DIV5_DIV 4
#define CLKID_FCLK_DIV7_DIV 5
#define NR_PLL_CLKS 11
#endif /* __A1_PLL_H */

View file

@ -295,10 +295,14 @@ static int meson_clk_pll_init(struct clk_hw *hw)
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
if (pll->init_count) {
meson_parm_write(clk->map, &pll->rst, 1);
if (MESON_PARM_APPLICABLE(&pll->rst))
meson_parm_write(clk->map, &pll->rst, 1);
regmap_multi_reg_write(clk->map, pll->init_regs,
pll->init_count);
meson_parm_write(clk->map, &pll->rst, 0);
if (MESON_PARM_APPLICABLE(&pll->rst))
meson_parm_write(clk->map, &pll->rst, 0);
}
return 0;
@ -309,8 +313,11 @@ static int meson_clk_pll_is_enabled(struct clk_hw *hw)
struct clk_regmap *clk = to_clk_regmap(hw);
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
if (meson_parm_read(clk->map, &pll->rst) ||
!meson_parm_read(clk->map, &pll->en) ||
if (MESON_PARM_APPLICABLE(&pll->rst) &&
meson_parm_read(clk->map, &pll->rst))
return 0;
if (!meson_parm_read(clk->map, &pll->en) ||
!meson_parm_read(clk->map, &pll->l))
return 0;
@ -341,13 +348,34 @@ static int meson_clk_pll_enable(struct clk_hw *hw)
return 0;
/* Make sure the pll is in reset */
meson_parm_write(clk->map, &pll->rst, 1);
if (MESON_PARM_APPLICABLE(&pll->rst))
meson_parm_write(clk->map, &pll->rst, 1);
/* Enable the pll */
meson_parm_write(clk->map, &pll->en, 1);
/* Take the pll out reset */
meson_parm_write(clk->map, &pll->rst, 0);
if (MESON_PARM_APPLICABLE(&pll->rst))
meson_parm_write(clk->map, &pll->rst, 0);
/*
* Compared with the previous SoCs, self-adaption current module
* is newly added for A1, keep the new power-on sequence to enable the
* PLL. The sequence is:
* 1. enable the pll, delay for 10us
* 2. enable the pll self-adaption current module, delay for 40us
* 3. enable the lock detect module
*/
if (MESON_PARM_APPLICABLE(&pll->current_en)) {
usleep_range(10, 20);
meson_parm_write(clk->map, &pll->current_en, 1);
usleep_range(40, 50);
}
if (MESON_PARM_APPLICABLE(&pll->l_detect)) {
meson_parm_write(clk->map, &pll->l_detect, 1);
meson_parm_write(clk->map, &pll->l_detect, 0);
}
if (meson_clk_pll_wait_lock(hw))
return -EIO;
@ -361,10 +389,15 @@ static void meson_clk_pll_disable(struct clk_hw *hw)
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
/* Put the pll is in reset */
meson_parm_write(clk->map, &pll->rst, 1);
if (MESON_PARM_APPLICABLE(&pll->rst))
meson_parm_write(clk->map, &pll->rst, 1);
/* Disable the pll */
meson_parm_write(clk->map, &pll->en, 0);
/* Disable PLL internal self-adaption current module */
if (MESON_PARM_APPLICABLE(&pll->current_en))
meson_parm_write(clk->map, &pll->current_en, 0);
}
static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,

View file

@ -36,6 +36,8 @@ struct meson_clk_pll_data {
struct parm frac;
struct parm l;
struct parm rst;
struct parm current_en;
struct parm l_detect;
const struct reg_sequence *init_regs;
unsigned int init_count;
const struct pll_params_table *table;

View file

@ -82,6 +82,7 @@ static u8 cken_get_parent(struct clk_hw *hw)
}
static const struct clk_ops cken_mux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = cken_get_parent,
.set_parent = dummy_clk_set_parent,
};

View file

@ -14,6 +14,7 @@
#include <linux/clk/renesas.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pm_clock.h>
@ -78,8 +79,8 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
struct mstp_clock_group *group = clock->group;
u32 bitmask = BIT(clock->bit_index);
unsigned long flags;
unsigned int i;
u32 value;
int ret;
spin_lock_irqsave(&group->lock, flags);
@ -101,19 +102,14 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
if (!enable || !group->mstpsr)
return 0;
for (i = 1000; i > 0; --i) {
if (!(cpg_mstp_read(group, group->mstpsr) & bitmask))
break;
cpu_relax();
}
if (!i) {
/* group->width_8bit is always false if group->mstpsr is present */
ret = readl_poll_timeout_atomic(group->mstpsr, value,
!(value & bitmask), 0, 10);
if (ret)
pr_err("%s: failed to enable %p[%d]\n", __func__,
group->smstpcr, clock->bit_index);
return -ETIMEDOUT;
}
return 0;
return ret;
}
static int cpg_mstp_clock_enable(struct clk_hw *hw)

View file

@ -170,6 +170,7 @@ static const struct mssr_mod_clk r8a779a0_mod_clks[] __initconst = {
DEF_MOD("msi3", 621, R8A779A0_CLK_MSO),
DEF_MOD("msi4", 622, R8A779A0_CLK_MSO),
DEF_MOD("msi5", 623, R8A779A0_CLK_MSO),
DEF_MOD("pwm0", 628, R8A779A0_CLK_S1D8),
DEF_MOD("rpc-if", 629, R8A779A0_CLK_RPCD2),
DEF_MOD("scif0", 702, R8A779A0_CLK_S1D8),
DEF_MOD("scif1", 703, R8A779A0_CLK_S1D8),

View file

@ -1121,6 +1121,7 @@ static int r9a06g032_clk_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_bitselect_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = r9a06g032_clk_mux_get_parent,
.set_parent = r9a06g032_clk_mux_set_parent,
};

View file

@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_address.h>
@ -196,8 +197,8 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
struct device *dev = priv->dev;
u32 bitmask = BIT(bit);
unsigned long flags;
unsigned int i;
u32 value;
int error;
dev_dbg(dev, "MSTP %u%02u/%pC %s\n", reg, bit, hw->clk,
enable ? "ON" : "OFF");
@ -228,19 +229,13 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
if (!enable || priv->reg_layout == CLK_REG_LAYOUT_RZ_A)
return 0;
for (i = 1000; i > 0; --i) {
if (!(readl(priv->base + priv->status_regs[reg]) & bitmask))
break;
cpu_relax();
}
if (!i) {
error = readl_poll_timeout_atomic(priv->base + priv->status_regs[reg],
value, !(value & bitmask), 0, 10);
if (error)
dev_err(dev, "Failed to enable SMSTP %p[%d]\n",
priv->base + priv->control_regs[reg], bit);
return -ETIMEDOUT;
}
return 0;
return error;
}
static int cpg_mstp_clock_enable(struct clk_hw *hw)
@ -896,8 +891,9 @@ static int cpg_mssr_suspend_noirq(struct device *dev)
static int cpg_mssr_resume_noirq(struct device *dev)
{
struct cpg_mssr_priv *priv = dev_get_drvdata(dev);
unsigned int reg, i;
unsigned int reg;
u32 mask, oldval, newval;
int error;
/* This is the best we can do to check for the presence of PSCI */
if (!psci_ops.cpu_suspend)
@ -935,14 +931,9 @@ static int cpg_mssr_resume_noirq(struct device *dev)
if (!mask)
continue;
for (i = 1000; i > 0; --i) {
oldval = readl(priv->base + priv->status_regs[reg]);
if (!(oldval & mask))
break;
cpu_relax();
}
if (!i)
error = readl_poll_timeout_atomic(priv->base + priv->status_regs[reg],
oldval, !(oldval & mask), 0, 10);
if (error)
dev_warn(dev, "Failed to enable SMSTP%u[0x%x]\n", reg,
oldval & mask);
}

View file

@ -603,10 +603,8 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
}
/* Output clock setting 1 */
writel(CPG_SIPLL5_CLK1_POSTDIV1_WEN | CPG_SIPLL5_CLK1_POSTDIV2_WEN |
CPG_SIPLL5_CLK1_REFDIV_WEN | (params.pl5_postdiv1 << 0) |
(params.pl5_postdiv2 << 4) | (params.pl5_refdiv << 8),
priv->base + CPG_SIPLL5_CLK1);
writel((params.pl5_postdiv1 << 0) | (params.pl5_postdiv2 << 4) |
(params.pl5_refdiv << 8), priv->base + CPG_SIPLL5_CLK1);
/* Output clock setting, SSCG modulation value setting 3 */
writel((params.pl5_fracin << 8), priv->base + CPG_SIPLL5_CLK3);
@ -905,9 +903,9 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable)
unsigned int reg = clock->off;
struct device *dev = priv->dev;
unsigned long flags;
unsigned int i;
u32 bitmask = BIT(clock->bit);
u32 value;
int error;
if (!clock->off) {
dev_dbg(dev, "%pC does not support ON/OFF\n", hw->clk);
@ -932,19 +930,13 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable)
if (!priv->info->has_clk_mon_regs)
return 0;
for (i = 1000; i > 0; --i) {
if (((readl(priv->base + CLK_MON_R(reg))) & bitmask))
break;
cpu_relax();
}
if (!i) {
error = readl_poll_timeout_atomic(priv->base + CLK_MON_R(reg), value,
value & bitmask, 0, 10);
if (error)
dev_err(dev, "Failed to enable CLK_ON %p\n",
priv->base + CLK_ON_R(reg));
return -ETIMEDOUT;
}
return 0;
return error;
}
static int rzg2l_mod_clock_enable(struct clk_hw *hw)

View file

@ -32,9 +32,6 @@
#define CPG_SIPLL5_STBY_RESETB_WEN BIT(16)
#define CPG_SIPLL5_STBY_SSCG_EN_WEN BIT(18)
#define CPG_SIPLL5_STBY_DOWNSPREAD_WEN BIT(20)
#define CPG_SIPLL5_CLK1_POSTDIV1_WEN BIT(16)
#define CPG_SIPLL5_CLK1_POSTDIV2_WEN BIT(20)
#define CPG_SIPLL5_CLK1_REFDIV_WEN BIT(24)
#define CPG_SIPLL5_CLK4_RESV_LSB (0xFF)
#define CPG_SIPLL5_MON_PLL5_LOCK BIT(4)

View file

@ -2,6 +2,7 @@
# Recent Exynos platforms should just select COMMON_CLK_SAMSUNG:
config COMMON_CLK_SAMSUNG
bool "Samsung Exynos clock controller support" if COMPILE_TEST
depends on OF
select S3C64XX_COMMON_CLK if ARM && ARCH_S3C64XX
select S5PV210_COMMON_CLK if ARM && ARCH_S5PV210
select EXYNOS_3250_COMMON_CLK if ARM && SOC_EXYNOS3250

View file

@ -55,6 +55,9 @@ static const struct of_device_id exynos_clkout_ids[] = {
}, {
.compatible = "samsung,exynos4210-pmu",
.data = &exynos_clkout_exynos4,
}, {
.compatible = "samsung,exynos4212-pmu",
.data = &exynos_clkout_exynos4,
}, {
.compatible = "samsung,exynos4412-pmu",
.data = &exynos_clkout_exynos4,

View file

@ -138,7 +138,8 @@
/* the exynos4 soc type */
enum exynos4_soc {
EXYNOS4210,
EXYNOS4X12,
EXYNOS4212,
EXYNOS4412,
};
/* list of PLLs to be registered */
@ -1205,6 +1206,24 @@ static const struct exynos_cpuclk_cfg_data e4210_armclk_d[] __initconst = {
{ 0 },
};
static const struct exynos_cpuclk_cfg_data e4212_armclk_d[] __initconst = {
{ 1500000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4210_CPU_DIV1(2, 6), },
{ 1400000, E4210_CPU_DIV0(2, 1, 6, 0, 7, 3), E4210_CPU_DIV1(2, 6), },
{ 1300000, E4210_CPU_DIV0(2, 1, 5, 0, 7, 3), E4210_CPU_DIV1(2, 5), },
{ 1200000, E4210_CPU_DIV0(2, 1, 5, 0, 7, 3), E4210_CPU_DIV1(2, 5), },
{ 1100000, E4210_CPU_DIV0(2, 1, 4, 0, 6, 3), E4210_CPU_DIV1(2, 4), },
{ 1000000, E4210_CPU_DIV0(1, 1, 4, 0, 5, 2), E4210_CPU_DIV1(2, 4), },
{ 900000, E4210_CPU_DIV0(1, 1, 3, 0, 5, 2), E4210_CPU_DIV1(2, 3), },
{ 800000, E4210_CPU_DIV0(1, 1, 3, 0, 5, 2), E4210_CPU_DIV1(2, 3), },
{ 700000, E4210_CPU_DIV0(1, 1, 3, 0, 4, 2), E4210_CPU_DIV1(2, 3), },
{ 600000, E4210_CPU_DIV0(1, 1, 3, 0, 4, 2), E4210_CPU_DIV1(2, 3), },
{ 500000, E4210_CPU_DIV0(1, 1, 3, 0, 4, 2), E4210_CPU_DIV1(2, 3), },
{ 400000, E4210_CPU_DIV0(1, 1, 3, 0, 4, 2), E4210_CPU_DIV1(2, 3), },
{ 300000, E4210_CPU_DIV0(1, 1, 2, 0, 4, 2), E4210_CPU_DIV1(2, 3), },
{ 200000, E4210_CPU_DIV0(1, 1, 1, 0, 3, 1), E4210_CPU_DIV1(2, 3), },
{ 0 },
};
#define E4412_CPU_DIV1(cores, hpm, copy) \
(((cores) << 8) | ((hpm) << 4) | ((copy) << 0))
@ -1233,6 +1252,11 @@ static const struct samsung_cpu_clock exynos4210_cpu_clks[] __initconst = {
CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1, 0x14200, e4210_armclk_d),
};
static const struct samsung_cpu_clock exynos4212_cpu_clks[] __initconst = {
CPU_CLK(CLK_ARM_CLK, "armclk", CLK_MOUT_APLL, CLK_MOUT_MPLL_USER_C,
CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1, 0x14200, e4212_armclk_d),
};
static const struct samsung_cpu_clock exynos4412_cpu_clks[] __initconst = {
CPU_CLK(CLK_ARM_CLK, "armclk", CLK_MOUT_APLL, CLK_MOUT_MPLL_USER_C,
CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1, 0x14200, e4412_armclk_d),
@ -1326,11 +1350,15 @@ static void __init exynos4_clk_init(struct device_node *np,
samsung_clk_register_fixed_factor(ctx,
exynos4x12_fixed_factor_clks,
ARRAY_SIZE(exynos4x12_fixed_factor_clks));
samsung_clk_register_cpu(ctx, exynos4412_cpu_clks,
ARRAY_SIZE(exynos4412_cpu_clks));
if (soc == EXYNOS4412)
samsung_clk_register_cpu(ctx, exynos4412_cpu_clks,
ARRAY_SIZE(exynos4412_cpu_clks));
else
samsung_clk_register_cpu(ctx, exynos4212_cpu_clks,
ARRAY_SIZE(exynos4212_cpu_clks));
}
if (soc == EXYNOS4X12)
if (soc == EXYNOS4212 || soc == EXYNOS4412)
exynos4x12_core_down_clock();
samsung_clk_extended_sleep_init(reg_base,
@ -1363,8 +1391,14 @@ static void __init exynos4210_clk_init(struct device_node *np)
}
CLK_OF_DECLARE(exynos4210_clk, "samsung,exynos4210-clock", exynos4210_clk_init);
static void __init exynos4212_clk_init(struct device_node *np)
{
exynos4_clk_init(np, EXYNOS4212);
}
CLK_OF_DECLARE(exynos4212_clk, "samsung,exynos4212-clock", exynos4212_clk_init);
static void __init exynos4412_clk_init(struct device_node *np)
{
exynos4_clk_init(np, EXYNOS4X12);
exynos4_clk_init(np, EXYNOS4412);
}
CLK_OF_DECLARE(exynos4412_clk, "samsung,exynos4412-clock", exynos4412_clk_init);

View file

@ -110,6 +110,7 @@ static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
static struct clk_ops gateclk_ops = {
.recalc_rate = socfpga_clk_recalc_rate,
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = socfpga_clk_get_parent,
.set_parent = socfpga_clk_set_parent,
};

View file

@ -9,13 +9,12 @@
#include "composite.h"
static long sprd_comp_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int sprd_comp_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct sprd_comp *cc = hw_to_sprd_comp(hw);
return sprd_div_helper_round_rate(&cc->common, &cc->div,
rate, parent_rate);
return divider_determine_rate(hw, req, NULL, cc->div.width, 0);
}
static unsigned long sprd_comp_recalc_rate(struct clk_hw *hw,
@ -53,7 +52,7 @@ const struct clk_ops sprd_comp_ops = {
.get_parent = sprd_comp_get_parent,
.set_parent = sprd_comp_set_parent,
.round_rate = sprd_comp_round_rate,
.determine_rate = sprd_comp_determine_rate,
.recalc_rate = sprd_comp_recalc_rate,
.set_rate = sprd_comp_set_rate,
};

View file

@ -9,23 +9,13 @@
#include "div.h"
long sprd_div_helper_round_rate(struct sprd_clk_common *common,
const struct sprd_div_internal *div,
unsigned long rate,
unsigned long *parent_rate)
{
return divider_round_rate(&common->hw, rate, parent_rate,
NULL, div->width, 0);
}
EXPORT_SYMBOL_GPL(sprd_div_helper_round_rate);
static long sprd_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct sprd_div *cd = hw_to_sprd_div(hw);
return sprd_div_helper_round_rate(&cd->common, &cd->div,
rate, parent_rate);
return divider_round_rate(&cd->common.hw, rate, parent_rate, NULL,
cd->div.width, 0);
}
unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common,

View file

@ -64,11 +64,6 @@ static inline struct sprd_div *hw_to_sprd_div(const struct clk_hw *hw)
return container_of(common, struct sprd_div, common);
}
long sprd_div_helper_round_rate(struct sprd_clk_common *common,
const struct sprd_div_internal *div,
unsigned long rate,
unsigned long *parent_rate);
unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common,
const struct sprd_div_internal *div,
unsigned long parent_rate);

View file

@ -119,20 +119,21 @@ clk_best_div(unsigned long parent_rate, unsigned long rate)
return parent_rate / rate + ((rate > (2*(parent_rate % rate))) ? 0 : 1);
}
static long flexgen_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int flexgen_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
unsigned long div;
/* Round div according to exact prate and wished rate */
div = clk_best_div(*prate, rate);
div = clk_best_div(req->best_parent_rate, req->rate);
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
*prate = rate * div;
return rate;
req->best_parent_rate = req->rate * div;
return 0;
}
return *prate / div;
req->rate = req->best_parent_rate / div;
return 0;
}
static unsigned long flexgen_recalc_rate(struct clk_hw *hw,
@ -197,7 +198,7 @@ static const struct clk_ops flexgen_ops = {
.is_enabled = flexgen_is_enabled,
.get_parent = flexgen_get_parent,
.set_parent = flexgen_set_parent,
.round_rate = flexgen_round_rate,
.determine_rate = flexgen_determine_rate,
.recalc_rate = flexgen_recalc_rate,
.set_rate = flexgen_set_rate,
};

View file

@ -275,6 +275,7 @@ static int clk_stm32_mux_set_parent(struct clk_hw *hw, u8 index)
}
const struct clk_ops clk_stm32_mux_ops = {
.determine_rate = __clk_mux_determine_rate,
.get_parent = clk_stm32_mux_get_parent,
.set_parent = clk_stm32_mux_set_parent,
};
@ -425,15 +426,15 @@ static unsigned long clk_stm32_composite_recalc_rate(struct clk_hw *hw,
composite->div_id, parent_rate);
}
static long clk_stm32_composite_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int clk_stm32_composite_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_stm32_composite *composite = to_clk_stm32_composite(hw);
const struct stm32_div_cfg *divider;
unsigned long rate;
if (composite->div_id == NO_STM32_DIV)
return rate;
return 0;
divider = &composite->clock_data->dividers[composite->div_id];
@ -444,14 +445,24 @@ static long clk_stm32_composite_round_rate(struct clk_hw *hw, unsigned long rate
val = readl(composite->base + divider->offset) >> divider->shift;
val &= clk_div_mask(divider->width);
return divider_ro_round_rate(hw, rate, prate, divider->table,
divider->width, divider->flags,
val);
rate = divider_ro_round_rate(hw, req->rate, &req->best_parent_rate,
divider->table, divider->width, divider->flags,
val);
if (rate < 0)
return rate;
req->rate = rate;
return 0;
}
return divider_round_rate_parent(hw, clk_hw_get_parent(hw),
rate, prate, divider->table,
divider->width, divider->flags);
rate = divider_round_rate_parent(hw, clk_hw_get_parent(hw),
req->rate, &req->best_parent_rate,
divider->table, divider->width, divider->flags);
if (rate < 0)
return rate;
req->rate = rate;
return 0;
}
static u8 clk_stm32_composite_get_parent(struct clk_hw *hw)
@ -601,7 +612,7 @@ static void clk_stm32_composite_disable_unused(struct clk_hw *hw)
const struct clk_ops clk_stm32_composite_ops = {
.set_rate = clk_stm32_composite_set_rate,
.recalc_rate = clk_stm32_composite_recalc_rate,
.round_rate = clk_stm32_composite_round_rate,
.determine_rate = clk_stm32_composite_determine_rate,
.get_parent = clk_stm32_composite_get_parent,
.set_parent = clk_stm32_composite_set_parent,
.enable = clk_stm32_composite_gate_enable,

View file

@ -528,11 +528,18 @@ static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents,
0x104, 0, 4, 24, 3, BIT(31),
CLK_SET_RATE_PARENT);
/*
* DSI output seems to work only when PLL_MIPI selected. Set it and prevent
* the mux from reparenting.
*/
#define SUN50I_A64_TCON0_CLK_REG 0x118
static const char * const tcon0_parents[] = { "pll-mipi", "pll-video0-2x" };
static const u8 tcon0_table[] = { 0, 2, };
static SUNXI_CCU_MUX_TABLE_WITH_GATE(tcon0_clk, "tcon0", tcon0_parents,
tcon0_table, 0x118, 24, 3, BIT(31),
CLK_SET_RATE_PARENT);
CLK_SET_RATE_PARENT |
CLK_SET_RATE_NO_REPARENT);
static const char * const tcon1_parents[] = { "pll-video0", "pll-video1" };
static const u8 tcon1_table[] = { 0, 2, };
@ -953,6 +960,11 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev)
writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
/* Set PLL MIPI as parent for TCON0 */
val = readl(reg + SUN50I_A64_TCON0_CLK_REG);
val &= ~GENMASK(26, 24);
writel(val | (0 << 24), reg + SUN50I_A64_TCON0_CLK_REG);
ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a64_ccu_desc);
if (ret)
return ret;

View file

@ -286,6 +286,7 @@ static const struct clk_ops tegra_bpmp_clk_mux_ops = {
.unprepare = tegra_bpmp_clk_unprepare,
.is_prepared = tegra_bpmp_clk_is_prepared,
.recalc_rate = tegra_bpmp_clk_recalc_rate,
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = tegra_bpmp_clk_set_parent,
.get_parent = tegra_bpmp_clk_get_parent,
};

View file

@ -45,16 +45,22 @@ static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
return div_ops->recalc_rate(div_hw, parent_rate);
}
static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
static int clk_periph_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct tegra_clk_periph *periph = to_clk_periph(hw);
const struct clk_ops *div_ops = periph->div_ops;
struct clk_hw *div_hw = &periph->divider.hw;
unsigned long rate;
__clk_hw_set_clk(div_hw, hw);
return div_ops->round_rate(div_hw, rate, prate);
rate = div_ops->round_rate(div_hw, req->rate, &req->best_parent_rate);
if (rate < 0)
return rate;
req->rate = rate;
return 0;
}
static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
@ -130,7 +136,7 @@ const struct clk_ops tegra_clk_periph_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
.recalc_rate = clk_periph_recalc_rate,
.round_rate = clk_periph_round_rate,
.determine_rate = clk_periph_determine_rate,
.set_rate = clk_periph_set_rate,
.is_enabled = clk_periph_is_enabled,
.enable = clk_periph_enable,
@ -140,6 +146,7 @@ const struct clk_ops tegra_clk_periph_ops = {
};
static const struct clk_ops tegra_clk_periph_nodiv_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
.is_enabled = clk_periph_is_enabled,
@ -153,7 +160,7 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
.recalc_rate = clk_periph_recalc_rate,
.round_rate = clk_periph_round_rate,
.determine_rate = clk_periph_determine_rate,
.set_rate = clk_periph_set_rate,
.restore_context = clk_periph_restore_context,
};

View file

@ -136,20 +136,28 @@ static void clk_super_mux_restore_context(struct clk_hw *hw)
}
static const struct clk_ops tegra_clk_super_mux_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_super_get_parent,
.set_parent = clk_super_set_parent,
.restore_context = clk_super_mux_restore_context,
};
static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int clk_super_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
struct clk_hw *div_hw = &super->frac_div.hw;
unsigned long rate;
__clk_hw_set_clk(div_hw, hw);
return super->div_ops->round_rate(div_hw, rate, parent_rate);
rate = super->div_ops->round_rate(div_hw, req->rate,
&req->best_parent_rate);
if (rate < 0)
return rate;
req->rate = rate;
return 0;
}
static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
@ -192,7 +200,7 @@ const struct clk_ops tegra_clk_super_ops = {
.get_parent = clk_super_get_parent,
.set_parent = clk_super_set_parent,
.set_rate = clk_super_set_rate,
.round_rate = clk_super_round_rate,
.determine_rate = clk_super_determine_rate,
.recalc_rate = clk_super_recalc_rate,
.restore_context = clk_super_restore_context,
};

View file

@ -344,6 +344,7 @@ static const struct clk_ops clk_prcmu_clkout_ops = {
.prepare = clk_prcmu_clkout_prepare,
.unprepare = clk_prcmu_clkout_unprepare,
.recalc_rate = clk_prcmu_clkout_recalc_rate,
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_prcmu_clkout_get_parent,
.set_parent = clk_prcmu_clkout_set_parent,
};

View file

@ -110,6 +110,7 @@ static const struct clk_ops clk_sysctrl_gate_fixed_rate_ops = {
};
static const struct clk_ops clk_sysctrl_set_parent_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = clk_sysctrl_set_parent,
.get_parent = clk_sysctrl_get_parent,
};

View file

@ -63,6 +63,7 @@ static int clk_sp810_timerclken_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops clk_sp810_timerclken_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = clk_sp810_timerclken_get_parent,
.set_parent = clk_sp810_timerclken_set_parent,
};

View file

@ -586,6 +586,7 @@ static u8 tegra_clk_sor_pad_get_parent(struct clk_hw *hw)
}
static const struct clk_ops tegra_clk_sor_pad_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = tegra_clk_sor_pad_set_parent,
.get_parent = tegra_clk_sor_pad_get_parent,
};

View file

@ -720,6 +720,7 @@ static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops cdns_sierra_pll_mux_ops = {
.determine_rate = __clk_mux_determine_rate,
.set_parent = cdns_sierra_pll_mux_set_parent,
.get_parent = cdns_sierra_pll_mux_get_parent,
};

View file

@ -1861,6 +1861,7 @@ static const struct clk_ops cdns_torrent_refclk_driver_ops = {
.enable = cdns_torrent_refclk_driver_enable,
.disable = cdns_torrent_refclk_driver_disable,
.is_enabled = cdns_torrent_refclk_driver_is_enabled,
.determine_rate = __clk_mux_determine_rate,
.set_parent = cdns_torrent_refclk_driver_set_parent,
.get_parent = cdns_torrent_refclk_driver_get_parent,
};

View file

@ -634,6 +634,7 @@ static int serdes_am654_clk_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops serdes_am654_clk_mux_ops = {
.determine_rate = __clk_mux_determine_rate,
.set_parent = serdes_am654_clk_mux_set_parent,
.get_parent = serdes_am654_clk_mux_get_parent,
};

View file

@ -801,6 +801,7 @@ static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index)
}
static const struct clk_ops wiz_clk_mux_ops = {
.determine_rate = __clk_mux_determine_rate,
.set_parent = wiz_clk_mux_set_parent,
.get_parent = wiz_clk_mux_get_parent,
};

View file

@ -214,6 +214,7 @@ static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index)
static const struct clk_ops sun6i_rtc_osc_ops = {
.recalc_rate = sun6i_rtc_osc_recalc_rate,
.determine_rate = clk_hw_determine_rate_no_reparent,
.get_parent = sun6i_rtc_osc_get_parent,
.set_parent = sun6i_rtc_osc_set_parent,

View file

@ -0,0 +1,115 @@
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
* Author: Jian Hu <jian.hu@amlogic.com>
*
* Copyright (c) 2023, SberDevices. All Rights Reserved.
* Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
*/
#ifndef __A1_PERIPHERALS_CLKC_H
#define __A1_PERIPHERALS_CLKC_H
#define CLKID_FIXPLL_IN 1
#define CLKID_USB_PHY_IN 2
#define CLKID_USB_CTRL_IN 3
#define CLKID_HIFIPLL_IN 4
#define CLKID_SYSPLL_IN 5
#define CLKID_DDS_IN 6
#define CLKID_SYS 7
#define CLKID_CLKTREE 8
#define CLKID_RESET_CTRL 9
#define CLKID_ANALOG_CTRL 10
#define CLKID_PWR_CTRL 11
#define CLKID_PAD_CTRL 12
#define CLKID_SYS_CTRL 13
#define CLKID_TEMP_SENSOR 14
#define CLKID_AM2AXI_DIV 15
#define CLKID_SPICC_B 16
#define CLKID_SPICC_A 17
#define CLKID_MSR 18
#define CLKID_AUDIO 19
#define CLKID_JTAG_CTRL 20
#define CLKID_SARADC_EN 21
#define CLKID_PWM_EF 22
#define CLKID_PWM_CD 23
#define CLKID_PWM_AB 24
#define CLKID_CEC 25
#define CLKID_I2C_S 26
#define CLKID_IR_CTRL 27
#define CLKID_I2C_M_D 28
#define CLKID_I2C_M_C 29
#define CLKID_I2C_M_B 30
#define CLKID_I2C_M_A 31
#define CLKID_ACODEC 32
#define CLKID_OTP 33
#define CLKID_SD_EMMC_A 34
#define CLKID_USB_PHY 35
#define CLKID_USB_CTRL 36
#define CLKID_SYS_DSPB 37
#define CLKID_SYS_DSPA 38
#define CLKID_DMA 39
#define CLKID_IRQ_CTRL 40
#define CLKID_NIC 41
#define CLKID_GIC 42
#define CLKID_UART_C 43
#define CLKID_UART_B 44
#define CLKID_UART_A 45
#define CLKID_SYS_PSRAM 46
#define CLKID_RSA 47
#define CLKID_CORESIGHT 48
#define CLKID_AM2AXI_VAD 49
#define CLKID_AUDIO_VAD 50
#define CLKID_AXI_DMC 51
#define CLKID_AXI_PSRAM 52
#define CLKID_RAMB 53
#define CLKID_RAMA 54
#define CLKID_AXI_SPIFC 55
#define CLKID_AXI_NIC 56
#define CLKID_AXI_DMA 57
#define CLKID_CPU_CTRL 58
#define CLKID_ROM 59
#define CLKID_PROC_I2C 60
#define CLKID_DSPA_EN 63
#define CLKID_DSPA_EN_NIC 64
#define CLKID_DSPB_EN 65
#define CLKID_DSPB_EN_NIC 66
#define CLKID_RTC 67
#define CLKID_CECA_32K 68
#define CLKID_CECB_32K 69
#define CLKID_24M 70
#define CLKID_12M 71
#define CLKID_FCLK_DIV2_DIVN 72
#define CLKID_GEN 73
#define CLKID_SARADC 75
#define CLKID_PWM_A 76
#define CLKID_PWM_B 77
#define CLKID_PWM_C 78
#define CLKID_PWM_D 79
#define CLKID_PWM_E 80
#define CLKID_PWM_F 81
#define CLKID_SPICC 82
#define CLKID_TS 83
#define CLKID_SPIFC 84
#define CLKID_USB_BUS 85
#define CLKID_SD_EMMC 86
#define CLKID_PSRAM 87
#define CLKID_DMC 88
#define CLKID_DSPA_A_SEL 95
#define CLKID_DSPA_B_SEL 98
#define CLKID_DSPB_A_SEL 101
#define CLKID_DSPB_B_SEL 104
#define CLKID_CECB_32K_SEL_PRE 113
#define CLKID_CECB_32K_SEL 114
#define CLKID_CECA_32K_SEL_PRE 117
#define CLKID_CECA_32K_SEL 118
#define CLKID_GEN_SEL 121
#define CLKID_PWM_A_SEL 124
#define CLKID_PWM_B_SEL 126
#define CLKID_PWM_C_SEL 128
#define CLKID_PWM_D_SEL 130
#define CLKID_PWM_E_SEL 132
#define CLKID_PWM_F_SEL 134
#define CLKID_SD_EMMC_SEL2 147
#endif /* __A1_PERIPHERALS_CLKC_H */

View file

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
* Author: Jian Hu <jian.hu@amlogic.com>
*
* Copyright (c) 2023, SberDevices. All Rights Reserved.
* Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
*/
#ifndef __A1_PLL_CLKC_H
#define __A1_PLL_CLKC_H
#define CLKID_FIXED_PLL 1
#define CLKID_FCLK_DIV2 6
#define CLKID_FCLK_DIV3 7
#define CLKID_FCLK_DIV5 8
#define CLKID_FCLK_DIV7 9
#define CLKID_HIFI_PLL 10
#endif /* __A1_PLL_CLKC_H */

View file

@ -1333,6 +1333,8 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw,
int clk_mux_determine_rate_flags(struct clk_hw *hw,
struct clk_rate_request *req,
unsigned long flags);
int clk_hw_determine_rate_no_reparent(struct clk_hw *hw,
struct clk_rate_request *req);
void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent);
void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate,
unsigned long *max_rate);

View file

@ -53,6 +53,7 @@
} \
if (__sleep_us) \
usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
cpu_relax(); \
} \
(cond) ? 0 : -ETIMEDOUT; \
})
@ -73,6 +74,10 @@
* Returns 0 on success and -ETIMEDOUT upon a timeout. In either
* case, the last read value at @args is stored in @val.
*
* This macro does not rely on timekeeping. Hence it is safe to call even when
* timekeeping is suspended, at the expense of an underestimation of wall clock
* time, which is rather minimal with a non-zero delay_us.
*
* When available, you'll probably want to use one of the specialized
* macros defined below rather than this macro directly.
*/
@ -80,21 +85,30 @@
delay_before_read, args...) \
({ \
u64 __timeout_us = (timeout_us); \
s64 __left_ns = __timeout_us * NSEC_PER_USEC; \
unsigned long __delay_us = (delay_us); \
ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
if (delay_before_read && __delay_us) \
u64 __delay_ns = __delay_us * NSEC_PER_USEC; \
if (delay_before_read && __delay_us) { \
udelay(__delay_us); \
if (__timeout_us) \
__left_ns -= __delay_ns; \
} \
for (;;) { \
(val) = op(args); \
if (cond) \
break; \
if (__timeout_us && \
ktime_compare(ktime_get(), __timeout) > 0) { \
if (__timeout_us && __left_ns < 0) { \
(val) = op(args); \
break; \
} \
if (__delay_us) \
if (__delay_us) { \
udelay(__delay_us); \
if (__timeout_us) \
__left_ns -= __delay_ns; \
} \
cpu_relax(); \
if (__timeout_us) \
__left_ns--; \
} \
(cond) ? 0 : -ETIMEDOUT; \
})

View file

@ -204,18 +204,19 @@ static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw,
return clk_aic32x4_pll_calc_rate(&settings, parent_rate);
}
static long clk_aic32x4_pll_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate)
static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_aic32x4_pll_muldiv settings;
int ret;
ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, *parent_rate);
ret = clk_aic32x4_pll_calc_muldiv(&settings, req->rate, req->best_parent_rate);
if (ret < 0)
return 0;
return -EINVAL;
return clk_aic32x4_pll_calc_rate(&settings, *parent_rate);
req->rate = clk_aic32x4_pll_calc_rate(&settings, req->best_parent_rate);
return 0;
}
static int clk_aic32x4_pll_set_rate(struct clk_hw *hw,
@ -266,7 +267,7 @@ static const struct clk_ops aic32x4_pll_ops = {
.unprepare = clk_aic32x4_pll_unprepare,
.is_prepared = clk_aic32x4_pll_is_prepared,
.recalc_rate = clk_aic32x4_pll_recalc_rate,
.round_rate = clk_aic32x4_pll_round_rate,
.determine_rate = clk_aic32x4_pll_determine_rate,
.set_rate = clk_aic32x4_pll_set_rate,
.set_parent = clk_aic32x4_pll_set_parent,
.get_parent = clk_aic32x4_pll_get_parent,
@ -292,6 +293,7 @@ static u8 clk_aic32x4_codec_clkin_get_parent(struct clk_hw *hw)
}
static const struct clk_ops aic32x4_codec_clkin_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = clk_aic32x4_codec_clkin_set_parent,
.get_parent = clk_aic32x4_codec_clkin_get_parent,
};
@ -326,16 +328,17 @@ static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate,
AIC32X4_DIV_MASK, divisor);
}
static long clk_aic32x4_div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static int clk_aic32x4_div_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
unsigned long divisor;
divisor = DIV_ROUND_UP(*parent_rate, rate);
divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate);
if (divisor > 128)
return -EINVAL;
return DIV_ROUND_UP(*parent_rate, divisor);
req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor);
return 0;
}
static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw,
@ -354,7 +357,7 @@ static const struct clk_ops aic32x4_div_ops = {
.prepare = clk_aic32x4_div_prepare,
.unprepare = clk_aic32x4_div_unprepare,
.set_rate = clk_aic32x4_div_set_rate,
.round_rate = clk_aic32x4_div_round_rate,
.determine_rate = clk_aic32x4_div_determine_rate,
.recalc_rate = clk_aic32x4_div_recalc_rate,
};
@ -382,7 +385,7 @@ static const struct clk_ops aic32x4_bdiv_ops = {
.set_parent = clk_aic32x4_bdiv_set_parent,
.get_parent = clk_aic32x4_bdiv_get_parent,
.set_rate = clk_aic32x4_div_set_rate,
.round_rate = clk_aic32x4_div_round_rate,
.determine_rate = clk_aic32x4_div_determine_rate,
.recalc_rate = clk_aic32x4_div_recalc_rate,
};