diff options
Diffstat (limited to 'drivers/clk')
91 files changed, 5029 insertions, 407 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index c44247d0b83e..43e55e983e66 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -63,7 +63,7 @@ config COMMON_CLK_RK808 config COMMON_CLK_HI655X tristate "Clock driver for Hi655x" if EXPERT depends on (MFD_HI655X_PMIC || COMPILE_TEST) - depends on REGMAP + select REGMAP default MFD_HI655X_PMIC ---help--- This driver supports the hi655x PMIC clock. This @@ -136,6 +136,39 @@ config COMMON_CLK_SI570 This driver supports Silicon Labs 570/571/598/599 programmable clock generators. +config COMMON_CLK_SI5324 + tristate "Clock driver for SiLabs 5324 and compatible devices" + depends on I2C + depends on OF + select REGMAP_I2C + help + ---help-- + This driver supports Silicon Labs 5324/5319/5328 programmable + clock generators. Dynamic programming of the oscillator is done + via I2C. + +config COMMON_CLK_IDT8T49N24X + tristate "Clock driver for IDT 8T49N24x" + depends on I2C + depends on OF + select REGMAP_I2C + help + ---help--- + This driver supports the IDT 8T49N24x universal frequency translator + product family. The only chip in the family that is currently + supported is the 8T49N241. The driver supports setting the rate for + all four outputs on the chip and automatically calculating/setting + the appropriate VCO value. + + The driver can read a full register map from the DT, + and will use that register map to initialize the attached part + (via I2C) when the system boots. Any configuration not supported + by the common clock framework must be done via the full register + map, including optimized settings. + + All outputs are currently assumed to be LVDS, unless overridden + in the full register map in the DT. + config COMMON_CLK_CDCE706 tristate "Clock driver for TI CDCE706 clock synthesizer" depends on I2C @@ -302,6 +335,7 @@ config COMMON_CLK_BD718XX config COMMON_CLK_FIXED_MMIO bool "Clock driver for Memory Mapped Fixed values" depends on COMMON_CLK && OF + depends on HAS_IOMEM help Support for Memory Mapped IO Fixed clocks diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 0138fb14e6f8..c44dda072e24 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o obj-$(CONFIG_COMMON_CLK_STM32H7) += clk-stm32h7.o obj-$(CONFIG_COMMON_CLK_STM32MP157) += clk-stm32mp1.o +obj-$(CONFIG_COMMON_CLK_SI5324) += si5324drv.o clk-si5324.o obj-$(CONFIG_ARCH_TANGO) += clk-tango4.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o @@ -77,6 +78,7 @@ obj-$(CONFIG_ARCH_BERLIN) += berlin/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_H8300) += h8300/ obj-$(CONFIG_ARCH_HISI) += hisilicon/ +obj-$(CONFIG_COMMON_CLK_IDT8T49N24X) += idt/ obj-y += imgtec/ obj-y += imx/ obj-y += ingenic/ @@ -117,4 +119,4 @@ obj-$(CONFIG_X86) += x86/ endif obj-$(CONFIG_ARCH_ZX) += zte/ obj-$(CONFIG_ARCH_ZYNQ) += zynq/ -obj-$(CONFIG_COMMON_CLK_ZYNQMP) += zynqmp/ +obj-$(CONFIG_COMMON_CLK_ZYNQMP) += zynqmp/ diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index e637bd6b295b..b4e6a7923233 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -967,9 +967,9 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw, return div; } -static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock, - unsigned long parent_rate, - u32 div) +static unsigned long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock, + unsigned long parent_rate, + u32 div) { const struct bcm2835_clock_data *data = clock->data; u64 temp; @@ -1756,7 +1756,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .load_mask = CM_PLLC_LOADPER, .hold_mask = CM_PLLC_HOLDPER, .fixed_divider = 1, - .flags = CLK_SET_RATE_PARENT), + .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT), /* * PLLD is the display PLL, used to drive DSI display panels. diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c index 274441e2ddb2..8f0619f362e3 100644 --- a/drivers/clk/bcm/clk-iproc-pll.c +++ b/drivers/clk/bcm/clk-iproc-pll.c @@ -736,6 +736,7 @@ void iproc_pll_clk_setup(struct device_node *node, const char *parent_name; struct iproc_clk *iclk_array; struct clk_hw_onecell_data *clk_data; + const char *clk_name; if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl)) return; @@ -783,7 +784,12 @@ void iproc_pll_clk_setup(struct device_node *node, iclk = &iclk_array[0]; iclk->pll = pll; - init.name = node->name; + ret = of_property_read_string_index(node, "clock-output-names", + 0, &clk_name); + if (WARN_ON(ret)) + goto err_pll_register; + + init.name = clk_name; init.ops = &iproc_pll_ops; init.flags = 0; parent_name = of_clk_get_parent_name(node, 0); @@ -803,13 +809,11 @@ void iproc_pll_clk_setup(struct device_node *node, goto err_pll_register; clk_data->hws[0] = &iclk->hw; + parent_name = clk_name; /* now initialize and register all leaf clocks */ for (i = 1; i < num_clks; i++) { - const char *clk_name; - memset(&init, 0, sizeof(init)); - parent_name = node->name; ret = of_property_read_string_index(node, "clock-output-names", i, &clk_name); diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 1654fd0eedc9..a790a8ca02ff 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -113,7 +113,7 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, RPI_FIRMWARE_ARM_CLK_ID, &val); if (ret) - return ret; + return 0; return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; } diff --git a/drivers/clk/berlin/bg2.c b/drivers/clk/berlin/bg2.c index bccdfa00fd37..67a9edbba29c 100644 --- a/drivers/clk/berlin/bg2.c +++ b/drivers/clk/berlin/bg2.c @@ -500,12 +500,15 @@ static void __init berlin2_clock_setup(struct device_node *np) int n, ret; clk_data = kzalloc(struct_size(clk_data, hws, MAX_CLKS), GFP_KERNEL); - if (!clk_data) + if (!clk_data) { + of_node_put(parent_np); return; + } clk_data->num = MAX_CLKS; hws = clk_data->hws; gbase = of_iomap(parent_np, 0); + of_node_put(parent_np); if (!gbase) return; diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c index e9518d35f262..dd2784bb75b6 100644 --- a/drivers/clk/berlin/bg2q.c +++ b/drivers/clk/berlin/bg2q.c @@ -286,19 +286,23 @@ static void __init berlin2q_clock_setup(struct device_node *np) int n, ret; clk_data = kzalloc(struct_size(clk_data, hws, MAX_CLKS), GFP_KERNEL); - if (!clk_data) + if (!clk_data) { + of_node_put(parent_np); return; + } clk_data->num = MAX_CLKS; hws = clk_data->hws; gbase = of_iomap(parent_np, 0); if (!gbase) { + of_node_put(parent_np); pr_err("%pOF: Unable to map global base\n", np); return; } /* BG2Q CPU PLL is not part of global registers */ cpupll_base = of_iomap(parent_np, 1); + of_node_put(parent_np); if (!cpupll_base) { pr_err("%pOF: Unable to map cpupll base\n", np); iounmap(gbase); diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c index 48122f574cb6..8a6c6b9c9a6a 100644 --- a/drivers/clk/clk-ast2600.c +++ b/drivers/clk/clk-ast2600.c @@ -579,7 +579,7 @@ static int aspeed_g6_clk_probe(struct platform_device *pdev) regmap_write(map, 0x308, 0x12000); /* 3x3 = 9 */ /* P-Bus (BCLK) clock divider */ - hw = clk_hw_register_divider_table(dev, "bclk", "hpll", 0, + hw = clk_hw_register_divider_table(dev, "bclk", "epll", 0, scu_g6_base + ASPEED_G6_CLK_SELECTION1, 20, 3, 0, ast2600_div_table, &aspeed_g6_clk_lock); diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index 308b353815e1..470d91d7314d 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -705,6 +705,10 @@ static int cdce925_probe(struct i2c_client *client, for (i = 0; i < data->chip_info->num_plls; ++i) { pll_clk_name[i] = kasprintf(GFP_KERNEL, "%pOFn.pll%d", client->dev.of_node, i); + if (!pll_clk_name[i]) { + err = -ENOMEM; + goto error; + } init.name = pll_clk_name[i]; data->pll[i].chip = data; data->pll[i].hw.init = &init; @@ -746,6 +750,10 @@ static int cdce925_probe(struct i2c_client *client, init.num_parents = 1; init.parent_names = &parent_name; /* Mux Y1 to input */ init.name = kasprintf(GFP_KERNEL, "%pOFn.Y1", client->dev.of_node); + if (!init.name) { + err = -ENOMEM; + goto error; + } data->clk[0].chip = data; data->clk[0].hw.init = &init; data->clk[0].index = 0; @@ -764,6 +772,10 @@ static int cdce925_probe(struct i2c_client *client, for (i = 1; i < data->chip_info->num_outputs; ++i) { init.name = kasprintf(GFP_KERNEL, "%pOFn.Y%d", client->dev.of_node, i+1); + if (!init.name) { + err = -ENOMEM; + goto error; + } data->clk[i].chip = data; data->clk[i].hw.init = &init; data->clk[i].index = i; diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 2ef819606c41..1a4e6340f95c 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -33,9 +33,12 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) else return rc; } - if (clkspec.np == node && !clk_supplier) + if (clkspec.np == node && !clk_supplier) { + of_node_put(clkspec.np); return 0; + } pclk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); if (IS_ERR(pclk)) { if (PTR_ERR(pclk) != -EPROBE_DEFER) pr_warn("clk: couldn't get parent clock %d for %pOF\n", @@ -48,10 +51,12 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) if (rc < 0) goto err; if (clkspec.np == node && !clk_supplier) { + of_node_put(clkspec.np); rc = 0; goto err; } clk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); if (IS_ERR(clk)) { if (PTR_ERR(clk) != -EPROBE_DEFER) pr_warn("clk: couldn't get assigned clock %d for %pOF\n", @@ -93,10 +98,13 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) else return rc; } - if (clkspec.np == node && !clk_supplier) + if (clkspec.np == node && !clk_supplier) { + of_node_put(clkspec.np); return 0; + } clk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); if (IS_ERR(clk)) { if (PTR_ERR(clk) != -EPROBE_DEFER) pr_warn("clk: couldn't get clock %d for %pOF\n", diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index f9d5b7334341..737aa70e2cb3 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -4,42 +4,101 @@ #include <linux/export.h> #include <linux/gfp.h> +struct devm_clk_state { + struct clk *clk; + void (*exit)(struct clk *clk); +}; + static void devm_clk_release(struct device *dev, void *res) { - clk_put(*(struct clk **)res); + struct devm_clk_state *state = res; + + if (state->exit) + state->exit(state->clk); + + clk_put(state->clk); } -struct clk *devm_clk_get(struct device *dev, const char *id) +static struct clk *__devm_clk_get(struct device *dev, const char *id, + struct clk *(*get)(struct device *dev, const char *id), + int (*init)(struct clk *clk), + void (*exit)(struct clk *clk)) { - struct clk **ptr, *clk; + struct devm_clk_state *state; + struct clk *clk; + int ret; - ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) + state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL); + if (!state) return ERR_PTR(-ENOMEM); - clk = clk_get(dev, id); - if (!IS_ERR(clk)) { - *ptr = clk; - devres_add(dev, ptr); - } else { - devres_free(ptr); + clk = get(dev, id); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto err_clk_get; } + if (init) { + ret = init(clk); + if (ret) + goto err_clk_init; + } + + state->clk = clk; + state->exit = exit; + + devres_add(dev, state); + return clk; + +err_clk_init: + + clk_put(clk); +err_clk_get: + + devres_free(state); + return ERR_PTR(ret); +} + +struct clk *devm_clk_get(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get, NULL, NULL); } EXPORT_SYMBOL(devm_clk_get); -struct clk *devm_clk_get_optional(struct device *dev, const char *id) +struct clk *devm_clk_get_prepared(struct device *dev, const char *id) { - struct clk *clk = devm_clk_get(dev, id); + return __devm_clk_get(dev, id, clk_get, clk_prepare, clk_unprepare); +} +EXPORT_SYMBOL_GPL(devm_clk_get_prepared); - if (clk == ERR_PTR(-ENOENT)) - return NULL; +struct clk *devm_clk_get_enabled(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get, + clk_prepare_enable, clk_disable_unprepare); +} +EXPORT_SYMBOL_GPL(devm_clk_get_enabled); - return clk; +struct clk *devm_clk_get_optional(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get_optional, NULL, NULL); } EXPORT_SYMBOL(devm_clk_get_optional); +struct clk *devm_clk_get_optional_prepared(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get_optional, + clk_prepare, clk_unprepare); +} +EXPORT_SYMBOL_GPL(devm_clk_get_optional_prepared); + +struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get_optional, + clk_prepare_enable, clk_disable_unprepare); +} +EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled); + struct clk_bulk_devres { struct clk_bulk_data *clks; int num_clks; @@ -146,18 +205,19 @@ EXPORT_SYMBOL(devm_clk_put); struct clk *devm_get_clk_from_child(struct device *dev, struct device_node *np, const char *con_id) { - struct clk **ptr, *clk; + struct devm_clk_state *state; + struct clk *clk; - ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) + state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL); + if (!state) return ERR_PTR(-ENOMEM); clk = of_clk_get_by_name(np, con_id); if (!IS_ERR(clk)) { - *ptr = clk; - devres_add(dev, ptr); + state->clk = clk; + devres_add(dev, state); } else { - devres_free(ptr); + devres_free(state); } return clk; diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 8b343e59dc61..4ef027f62880 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -211,8 +211,9 @@ void __init of_fixed_factor_clk_setup(struct device_node *node) { _of_fixed_factor_clk_setup(node); } -CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock", - of_fixed_factor_clk_setup); + +CLK_OF_DECLARE_DRIVER(fixed_factor_clk, "fixed-factor-clock", + of_fixed_factor_clk_setup); static int of_fixed_factor_clk_remove(struct platform_device *pdev) { diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 1b99fc962745..289fc4d959ab 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -56,7 +56,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable) { struct clk_gate *gate = to_clk_gate(hw); int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; - unsigned long uninitialized_var(flags); + unsigned long flags; u32 reg; set ^= enable; diff --git a/drivers/clk/clk-npcm7xx.c b/drivers/clk/clk-npcm7xx.c index 27a86b7a34db..c82df105b0a2 100644 --- a/drivers/clk/clk-npcm7xx.c +++ b/drivers/clk/clk-npcm7xx.c @@ -647,7 +647,7 @@ static void __init npcm7xx_clk_init(struct device_node *clk_np) return; npcm7xx_init_fail: - kfree(npcm7xx_clk_data->hws); + kfree(npcm7xx_clk_data); npcm7xx_init_np_err: iounmap(clk_base); npcm7xx_init_error: diff --git a/drivers/clk/clk-oxnas.c b/drivers/clk/clk-oxnas.c index 78d5ea669fea..2fe36f579ac5 100644 --- a/drivers/clk/clk-oxnas.c +++ b/drivers/clk/clk-oxnas.c @@ -207,7 +207,7 @@ static const struct of_device_id oxnas_stdclk_dt_ids[] = { static int oxnas_stdclk_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device_node *np = pdev->dev.of_node, *parent_np; const struct oxnas_stdclk_data *data; const struct of_device_id *id; struct regmap *regmap; @@ -219,7 +219,9 @@ static int oxnas_stdclk_probe(struct platform_device *pdev) return -ENODEV; data = id->data; - regmap = syscon_node_to_regmap(of_get_parent(np)); + parent_np = of_get_parent(np); + regmap = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); if (IS_ERR(regmap)) { dev_err(&pdev->dev, "failed to have parent regmap\n"); return PTR_ERR(regmap); diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index e3cdb4a282fe..d68de3773eb1 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -170,6 +170,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev) sclk->info = handle->clk_ops->info_get(handle, idx); if (!sclk->info) { dev_dbg(dev, "invalid clock info for idx %d\n", idx); + devm_kfree(dev, sclk); continue; } diff --git a/drivers/clk/clk-si5324.c b/drivers/clk/clk-si5324.c new file mode 100644 index 000000000000..7cfe75d7e6a4 --- /dev/null +++ b/drivers/clk/clk-si5324.c @@ -0,0 +1,1227 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * clk-si5324.c - Si5324 clock driver + * + * Copyright (C) 2017-2018 Xilinx, Inc. + * + * Author: Venkateshwar Rao G <vgannava.xilinx.com> + * Leon Woestenberg <leon@sidebranch.com> + */ + +#include <asm/div64.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/rational.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include "clk-si5324.h" +#include "si5324.h" +#include "si5324drv.h" + +struct si5324_driver_data; + +/** + * struct si5324_parameters - si5324 core parameters + * + * @n1_hs_min: Minimum high-speed n1 output divider + * @n1_hs_max: Maximum high-speed n1 output divider + * @n1_hs: n1 high-speed output divider + * @nc1_ls_min: Minimum low-speed clkout1 output divider + * @nc1_ls_max: Maximum low-speed clkout1 output divider + * @nc1_ls: Clkout1 low-speed output divider + * @nc2_ls_min: Minimum low-speed clkout2 output divider + * @nc2_ls_max: Maximum low-speed clkout2 output divider + * @nc2_ls: Clkout2 low-speed output divider + * @n2_hs: High-speed feedback divider + * @n2_ls_min: Minimum low-speed feedback divider + * @n2_ls_max: Maximum low-speed feedback divider + * @n2_ls: Low-speed feedback divider + * @n31_min: Minimum input divider for clk1 + * @n31_max: Maximum input divider for clk1 + * @n31: Input divider for clk1 + * @n32_min: Minimum input divider for clk2 + * @n32_max: Maximum input divider for clk2 + * @n32: Input divider for clk2 + * @fin: Input frequency + * @fout: Output frequency + * @fosc: Osc frequency + * @best_delta_fout: Delta out frequency + * @best_fout: Best output frequency + * @best_n1_hs: Best high speed output divider + * @best_nc1_ls: Best low speed clkout1 divider + * @best_n2_hs: Best high speed feedback divider + * @best_n2_ls: Best low speed feedback divider + * @best_n3: Best input clock divider + * @valid: Validility + */ +struct si5324_parameters { + u32 n1_hs_min; + u32 n1_hs_max; + u32 n1_hs; + u32 nc1_ls_min; + u32 nc1_ls_max; + u32 nc1_ls; + u32 nc2_ls_min; + u32 nc2_ls_max; + u32 nc2_ls; + u32 n2_hs; + u32 n2_ls_min; + u32 n2_ls_max; + u32 n2_ls; + u32 n31_min; + u32 n31_max; + u32 n31; + u32 n32_min; + u32 n32_max; + u32 n32; + u64 fin; + u64 fout; + u64 fosc; + u64 best_delta_fout; + u64 best_fout; + u32 best_n1_hs; + u32 best_nc1_ls; + u32 best_n2_hs; + u32 best_n2_ls; + u32 best_n3; + int valid; +}; + +/** + * struct si5324_hw_data - Clock parameters + * + * @hw: Handle between common and hardware-specific interfaces + * @drvdata: Driver private data + * @num: Differential pair clock number + */ +struct si5324_hw_data { + struct clk_hw hw; + struct si5324_driver_data *drvdata; + unsigned char num; +}; + +/** + * struct si5324_driver_data - Driver parameters + * @client: I2C client pointer + * @regmap: Device's regmap + * @onecell: Clock onecell data + * @params: Device parameters + * @pxtal: Clock + * @pxtal_name: Clock name + * @xtal: Reference clock + * @pclkin1: Clock in 1 + * @pclkin1_name: Clock in 1 name + * @clkin1: Differential input clock 1 + * @pclkin2: Clock in 2 + * @pclkin2_name: Clock in 2 name + * @clkin2: Differential input clock 2 + * @pll: Pll clock + * @clkout: Output clock + * @rate_clkout0: Clock out 0 rate + * @rate_clkout1: Clock out 1 rate + */ +struct si5324_driver_data { + struct i2c_client *client; + struct regmap *regmap; + struct clk_onecell_data onecell; + struct si5324_parameters params; + struct clk *pxtal; + const char *pxtal_name; + struct clk_hw xtal; + struct clk *pclkin1; + const char *pclkin1_name; + struct clk_hw clkin1; + struct clk *pclkin2; + const char *pclkin2_name; + struct clk_hw clkin2; + struct si5324_hw_data pll; + struct si5324_hw_data *clkout; + unsigned long rate_clkout0; + unsigned long rate_clkout1; +}; + +static const char * const si5324_input_names[] = { + "xtal", "clkin1", "clkin2" +}; + +static const char * const si5324_pll_name = "pll"; + +static const char * const si5324_clkout_names[] = { + "clk0", "clk1" +}; + +enum si53xx_variant { + si5319, + si5324, + si5328 +}; + +static const char * const si53xx_variant_name[] = { + "si5319", "si5324", "si5328" +}; + +/** + * si5324_reg_read - Read a single si5324 register. + * + * @drvdata: Device to read from. + * @reg: Register to read. + * + * This function reads data from a single register + * + * Return: Data of the register on success, error number on failure + */ +static inline int +si5324_reg_read(struct si5324_driver_data *drvdata, u8 reg) +{ + u32 val; + int ret; + + ret = regmap_read(drvdata->regmap, reg, &val); + if (ret < 0) { + dev_err(&drvdata->client->dev, + "unable to read from reg%02x\n", reg); + return ret; + } + + return (u8)val; +} + +/** + * si5324_bulk_read - Read multiple si5324 registers + * + * @drvdata: Device to read from + * @reg: First register to be read from + * @count: Number of registers + * @buf: Pointer to store read value + * + * This function reads from multiple registers which are in + * sequential order + * + * Return: Number of bytes read + */ +static inline int si5324_bulk_read(struct si5324_driver_data *drvdata, + u8 reg, u8 count, u8 *buf) +{ + return regmap_bulk_read(drvdata->regmap, reg, buf, count); +} + +/** + * si5324_reg_write - Write a single si5324 register. + * + * @drvdata: Device to write to. + * @reg: Register to write to. + * @val: Value to write. + * + * This function writes into a single register + * + * Return: Zero on success, a negative error number on failure. + * + */ +static inline int si5324_reg_write(struct si5324_driver_data *drvdata, + u8 reg, u8 val) +{ + int ret = regmap_write(drvdata->regmap, reg, val); + + dev_dbg(&drvdata->client->dev, "%s 0x%02x @%02d\n", __func__, + (int)val, (int)reg); + return ret; +} + +/** + * si5324_bulk_write - Write into multiple si5324 registers + * + * @drvdata: Device to write to + * @reg: First register + * @count: Number of registers + * @buf: Block of data to be written + * + * This function writes into multiple registers. + * + * Return: Zero on success, a negative error number on failure. + */ +static inline int si5324_bulk_write(struct si5324_driver_data *drvdata, + u8 reg, u8 count, const u8 *buf) +{ + return regmap_raw_write(drvdata->regmap, reg, buf, count); +} + +/** + * si5324_set_bits - Set the value of a bitfield in a si5324 register + * + * @drvdata: Device to write to. + * @reg: Register to write to. + * @mask: Mask of bits to set. + * @val: Value to set (unshifted) + * + * This function set particular bits in register + * + * Return: Zero on success, a negative error number on failure. + */ +static inline int si5324_set_bits(struct si5324_driver_data *drvdata, + u8 reg, u8 mask, u8 val) +{ + return regmap_update_bits(drvdata->regmap, reg, mask, val); +} + +/** + * si5324_bulk_scatter_write - Write into multiple si5324 registers + * + * @drvdata: Device to write to + * @count: Number of registers + * @buf: Register and data to write + * + * This function writes into multiple registers which are need not + * to be in sequential order. + * + * Return: Number of bytes written + */ +static inline int +si5324_bulk_scatter_write(struct si5324_driver_data *drvdata, + u8 count, const u8 *buf) +{ + int i; + int ret = 0; + + for (i = 0; i < count; i++) { + ret = si5324_reg_write(drvdata, buf[i * 2], buf[i * 2 + 1]); + if (ret) + return ret; + } + return ret; +} + +/** + * si5324_initialize - Initializes si5324 device + * + * @drvdata: Device instance + * + * This function initializes si5324 with the following settings + * Keep reset asserted for 20ms + * 1. freerun mode + * 2. Disable output clocks during calibration + * 3. Clock selection mode : default value, manual + * 4. output signal format : LVDS for clkout1, disable clkout2 + * 5. CS_CA pin in ignored + * 6. Set lock time to 13.3ms + * 7. Enables the fastlock. + * + * Return: Zero on success, negative number on failure. + */ +static int si5324_initialize(struct si5324_driver_data *drvdata) +{ + int ret = 0; + + si5324_set_bits(drvdata, SI5324_RESET_CALIB, + SI5324_RST_ALL, SI5324_RST_ALL); + msleep(SI5324_RESET_DELAY_MS); + si5324_set_bits(drvdata, SI5324_RESET_CALIB, SI5324_RST_ALL, 0); + msleep(SI5324_RESET_DELAY_MS); + + ret = si5324_reg_read(drvdata, SI5324_CONTROL); + if (ret < 0) + return ret; + + si5324_reg_write(drvdata, SI5324_CONTROL, + (ret | SI5324_CONTROL_FREE_RUN)); + + ret = si5324_reg_read(drvdata, SI5324_CKSEL); + if (ret < 0) + return ret; + + si5324_reg_write(drvdata, SI5324_CKSEL, (ret | SI5324_CKSEL_SQL_ICAL)); + si5324_reg_write(drvdata, SI3324_AUTOSEL, SI5324_AUTOSEL_DEF); + si5324_reg_write(drvdata, SI5324_OUTPUT_SIGFMT, + SI5324_OUTPUT_SF1_DEFAULT); + + ret = si5324_reg_read(drvdata, SI5324_DSBL_CLKOUT); + if (ret < 0) + return ret; + + si5324_reg_write(drvdata, SI5324_DSBL_CLKOUT, + (ret | SI5324_DSBL_CLKOUT2)); + ret = si5324_reg_read(drvdata, SI5324_POWERDOWN); + if (ret < 0) + return ret; + + si5324_reg_write(drvdata, SI5324_POWERDOWN, (ret | SI5324_PD_CK2)); + si5324_reg_write(drvdata, SI5324_FOS_LOCKT, SI5324_FOS_DEFAULT); + + ret = si5324_reg_read(drvdata, SI5324_CK_ACTV_SEL); + if (ret < 0) + return ret; + + si5324_reg_write(drvdata, SI5324_CK_ACTV_SEL, SI5324_CK_DEFAULT); + ret = si5324_reg_read(drvdata, SI5324_FASTLOCK); + if (ret < 0) + return ret; + + si5324_reg_write(drvdata, SI5324_FASTLOCK, (ret | SI5324_FASTLOCK_EN)); + return 0; +} + +/** + * si5324_read_parameters - Reads clock divider parameters + * + * @drvdata: Device to read from + * + * This function reads the clock divider parameters into driver structure. + * + * Following table gives the buffer index, register number and + * register name with bit fields + * 0 25 N1_HS[2:0] + * 6 31 NC1_LS[19:16] + * 7 32 NC1_LS[15:8] + * 8 33 NC1_LS[7:0] + * 9 34 NC2_LS[19:16] + * 10 35 NC2_LS[15:8] + * 11 36 NC2_LS[7:0] + * 15 40 N2_HS[2:0] N2_LS[19:16] + * 16 41 N2_LS[15:8] + * 17 42 N2_LS[7:0] + * 18 43 N31[18:16] + * 19 44 N31[15:8] + * 20 45 N31[7:0] + * 21 46 N32[18:16] + * 22 47 N32[15:8] + * 23 48 N32[7:0] + */ +static void si5324_read_parameters(struct si5324_driver_data *drvdata) +{ + u8 buf[SI5324_PARAM_LEN]; + + si5324_bulk_read(drvdata, SI5324_N1_HS, SI5324_N1_PARAM_LEN, &buf[0]); + si5324_bulk_read(drvdata, SI5324_NC1_LS_H, SI5324_NC_PARAM_LEN, + &buf[6]); + si5324_bulk_read(drvdata, SI5324_N2_HS_LS_H, SI5324_N2_PARAM_LEN, + &buf[15]); + + drvdata->params.n1_hs = (buf[0] >> SI5324_N1_HS_VAL_SHIFT); + drvdata->params.n1_hs += 4; + + drvdata->params.nc1_ls = ((buf[6] & SI5324_DIV_LS_MASK) << + SI5324_HSHIFT) | (buf[7] << SI5324_LSHIFT) | + buf[8]; + drvdata->params.nc1_ls += 1; + drvdata->params.nc2_ls = ((buf[9] & SI5324_DIV_LS_MASK) << + SI5324_HSHIFT) | (buf[10] << SI5324_LSHIFT) | + buf[11]; + drvdata->params.nc2_ls += 1; + drvdata->params.n2_ls = ((buf[15] & SI5324_DIV_LS_MASK) << + SI5324_HSHIFT) | (buf[16] << SI5324_LSHIFT) | + buf[17]; + drvdata->params.n2_ls += 1; + drvdata->params.n2_hs = buf[15] >> SI5324_N2_HS_LS_H_VAL_SHIFT; + drvdata->params.n2_hs += 4; + drvdata->params.n31 = ((buf[18] & SI5324_DIV_LS_MASK) << + SI5324_HSHIFT) | (buf[19] << SI5324_LSHIFT) | + buf[20]; + drvdata->params.n31 += 1; + drvdata->params.n32 = ((buf[21] & SI5324_DIV_LS_MASK) << + SI5324_HSHIFT) | (buf[22] << SI5324_LSHIFT) | + buf[23]; + drvdata->params.n32 += 1; + drvdata->params.valid = 1; +} + +static bool si5324_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return true; +} + +/** + * si5324_regmap_is_readable - Checks the register is readable or not + * + * @dev: Registered device + * @reg: Register offset + * + * Checks the register is readable or not. + * + * Return: True if the register is reabdle, False if it is not readable. + */ +static bool si5324_regmap_is_readable(struct device *dev, unsigned int reg) +{ + if ((reg > SI5324_POWERDOWN && reg < SI5324_FOS_LOCKT) || + (reg > SI5324_N1_HS && reg < SI5324_NC1_LS_H) || + (reg > SI5324_NC2_LS_L && reg < SI5324_N2_HS_LS_H) || + (reg > SI5324_N32_CLKIN_L && reg < SI5324_FOS_CLKIN_RATE) || + (reg > SI5324_FOS_CLKIN_RATE && reg < SI5324_PLL_ACTV_CLK) || + reg > SI5324_SKEW2) + return false; + + return true; +} + +/** + * si5324_regmap_is_writable - Checks the register is writable or not + * + * @dev: Registered device + * @reg: Register offset + * + * Checks the register is writable or not. + * + * Return: True if the register is writeable, False if it's not writeable. + */ +static bool si5324_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + if ((reg > SI5324_POWERDOWN && reg < SI5324_FOS_LOCKT) || + (reg > SI5324_N1_HS && reg < SI5324_NC1_LS_H) || + (reg > SI5324_NC2_LS_L && reg < SI5324_N2_HS_LS_H) || + (reg > SI5324_N32_CLKIN_L && reg < SI5324_FOS_CLKIN_RATE) || + (reg > SI5324_FOS_CLKIN_RATE && reg < SI5324_PLL_ACTV_CLK) || + reg > SI5324_SKEW2 || + (reg >= SI5324_PLL_ACTV_CLK && reg <= SI5324_CLKIN_LOL_STATUS) || + (reg >= SI5324_PARTNO_H && reg <= SI5324_PARTNO_L)) + return false; + + return true; +} + +static const struct regmap_config si5324_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 144, + .writeable_reg = si5324_regmap_is_writeable, + .readable_reg = si5324_regmap_is_readable, + .volatile_reg = si5324_regmap_is_volatile, +}; + +static int si5324_xtal_prepare(struct clk_hw *hw) +{ + return 0; +} + +static void si5324_xtal_unprepare(struct clk_hw *hw) +{ +} + +static const struct clk_ops si5324_xtal_ops = { + .prepare = si5324_xtal_prepare, + .unprepare = si5324_xtal_unprepare, +}; + +/** + * si5324_clkin_prepare - Prepare the clkin + * + * @hw: Handle between common and hardware-specific interfaces + * + * This function enables the particular clk + * + * Return: Zero on success, a negative error number on failure. + */ +static int si5324_clkin_prepare(struct clk_hw *hw) +{ + int ret = 0; + struct si5324_driver_data *drvdata; + struct si5324_hw_data *hwdata = + container_of(hw, struct si5324_hw_data, hw); + + if (hwdata->num == SI5324_CLKIN1) { + drvdata = container_of(hw, struct si5324_driver_data, clkin1); + ret = si5324_set_bits(drvdata, SI5324_CONTROL, + SI5324_CONTROL_FREE_RUN, 0); + ret = si5324_set_bits(drvdata, SI5324_POWERDOWN, SI5324_PD_CK1 | + SI5324_PD_CK2, SI5324_PD_CK2); + } else if (hwdata->num == SI5324_CLKIN2) { + drvdata = container_of(hw, struct si5324_driver_data, clkin2); + ret = si5324_set_bits(drvdata, SI5324_CONTROL, + SI5324_CONTROL_FREE_RUN, 0); + ret = si5324_set_bits(drvdata, SI5324_POWERDOWN, SI5324_PD_CK1 | + SI5324_PD_CK2, SI5324_PD_CK1); + } + + return ret; +} + +/** + * si5324_clkin_unprepare - Unprepare the clkin + * + * @hw: Clock hardware + * + * This function enables the particular clk. + */ +static void si5324_clkin_unprepare(struct clk_hw *hw) +{ + struct si5324_driver_data *drvdata; + struct si5324_hw_data *hwdata = + container_of(hw, struct si5324_hw_data, hw); + + if (hwdata->num == SI5324_CLKIN1) { + drvdata = container_of(hw, struct si5324_driver_data, clkin1); + si5324_set_bits(drvdata, SI5324_POWERDOWN, + SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK1); + } else if (hwdata->num == SI5324_CLKIN2) { + drvdata = container_of(hw, struct si5324_driver_data, clkin2); + si5324_set_bits(drvdata, SI5324_POWERDOWN, + SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK1); + } +} + +static unsigned long si5324_clkin_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 0; +} + +static const struct clk_ops si5324_clkin_ops = { + .prepare = si5324_clkin_prepare, + .unprepare = si5324_clkin_unprepare, + .recalc_rate = si5324_clkin_recalc_rate, +}; + +static int si5324_pll_reparent(struct si5324_driver_data *drvdata, + int num, enum si5324_pll_src parent) +{ + if (parent == SI5324_PLL_SRC_XTAL) { + si5324_set_bits(drvdata, SI5324_CONTROL, + SI5324_CONTROL_FREE_RUN, + SI5324_CONTROL_FREE_RUN); + si5324_set_bits(drvdata, SI5324_POWERDOWN, + SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK1); + si5324_set_bits(drvdata, SI5324_CKSEL, + SI5324_CK_SEL << SI5324_CKSEL_SHIFT, + 1 << SI5324_CKSEL_SHIFT); + } else if (parent == SI5324_PLL_SRC_CLKIN1) { + si5324_set_bits(drvdata, SI5324_CONTROL, + SI5324_CONTROL_FREE_RUN, 0); + si5324_set_bits(drvdata, SI5324_POWERDOWN, + SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK2); + si5324_set_bits(drvdata, SI5324_CKSEL, + SI5324_CK_SEL << SI5324_CKSEL_SHIFT, 0); + } else if (parent == SI5324_PLL_SRC_CLKIN2) { + si5324_set_bits(drvdata, SI5324_CONTROL, + SI5324_CONTROL_FREE_RUN, 0); + si5324_set_bits(drvdata, SI5324_POWERDOWN, + SI5324_PD_CK1 | SI5324_PD_CK2, SI5324_PD_CK1); + si5324_set_bits(drvdata, SI5324_CKSEL, + SI5324_CK_SEL << SI5324_CKSEL_SHIFT, + 1 << SI5324_CKSEL_SHIFT); + } + + return 0; +} + +static unsigned char si5324_pll_get_parent(struct clk_hw *hw) +{ + return 0; +} + +/** + * si5324_pll_set_parent - Set parent of clock + * + * @hw: Handle between common and hardware-specific interfaces + * @index: Parent index + * + * This function sets the paraent of clock. + * + * Return: 0 on success, negative error number on failure + */ +static int si5324_pll_set_parent(struct clk_hw *hw, u8 index) +{ + struct si5324_hw_data *hwdata = + container_of(hw, struct si5324_hw_data, hw); + enum si5324_pll_src parent; + + if (index == SI5324_SRC_XTAL) + parent = SI5324_PLL_SRC_XTAL; + else if (index == SI5324_SRC_CLKIN1) + parent = SI5324_PLL_SRC_CLKIN1; + else if (index == SI5324_SRC_CLKIN2) + parent = SI5324_PLL_SRC_CLKIN2; + else + return -EINVAL; + + return si5324_pll_reparent(hwdata->drvdata, hwdata->num, parent); +} + +/** + * si5324_pll_recalc_rate - Recalculate clock frequency + * + * @hw: Handle between common and hardware-specific interfaces + * @parent_rate: Clock frequency of parent clock + * + * This function recalculate clock frequency. + * + * Return: Current clock frequency + */ +static unsigned long si5324_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long rate; + struct si5324_hw_data *hwdata = + container_of(hw, struct si5324_hw_data, hw); + + if (!hwdata->drvdata->params.valid) + si5324_read_parameters(hwdata->drvdata); + WARN_ON(!hwdata->drvdata->params.valid); + + rate = parent_rate * hwdata->drvdata->params.n2_ls * + hwdata->drvdata->params.n2_hs; + + dev_dbg(&hwdata->drvdata->client->dev, + "%s - %s: n2_ls = %u, n2_hs = %u, parent_rate = %lu, rate = %lu\n", + __func__, clk_hw_get_name(hw), + hwdata->drvdata->params.n2_ls, hwdata->drvdata->params.n2_hs, + parent_rate, (unsigned long)rate); + + return rate; +} + +static long si5324_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static int si5324_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return 0; +} + +static const struct clk_ops si5324_pll_ops = { + .set_parent = si5324_pll_set_parent, + .get_parent = si5324_pll_get_parent, + .recalc_rate = si5324_pll_recalc_rate, + .round_rate = si5324_pll_round_rate, + .set_rate = si5324_pll_set_rate, +}; + +static int si5324_clkout_set_drive_strength( + struct si5324_driver_data *drvdata, int num, + enum si5324_drive_strength drive) +{ + return 0; +} + +static int si5324_clkout_prepare(struct clk_hw *hw) +{ + return 0; +} + +static void si5324_clkout_unprepare(struct clk_hw *hw) +{ +} + +static unsigned long si5324_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long rate; + + struct si5324_hw_data *hwdata = + container_of(hw, struct si5324_hw_data, hw); + + rate = hwdata->drvdata->rate_clkout0; + + return rate; +} + +/** + * si5324_clkout_round_rate - selects the closest value to requested one. + * + * @hw: Handle between common and hardware-specific interfaces + * @rate: Clock rate + * @parent_rate: Parent clock rate + * + * This function selects the rate closest to the requested one. + * + * Return: Clock rate on success, negative error number on failure + */ +static long si5324_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 ncn_ls, n2_ls, n3n, actual_rate; + u8 n1_hs, n2_hs, bwsel; + int ret; + + ret = si5324_calcfreqsettings(SI5324_REF_CLOCK, rate, &actual_rate, + &n1_hs, &ncn_ls, &n2_hs, &n2_ls, &n3n, + &bwsel); + if (ret < 0) + return ret; + + return actual_rate; +} + +static int si5324_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct si5324_hw_data *hwdata = + container_of(hw, struct si5324_hw_data, hw); + + u32 ncn_ls, n2_ls, n3n, actual_rate; + u8 n1_hs, n2_hs, bwsel, buf[SI5324_OUT_REGS * 2]; + int i, ret, rc; + + ret = si5324_calcfreqsettings(SI5324_REF_CLOCK, rate, &actual_rate, + &n1_hs, &ncn_ls, &n2_hs, &n2_ls, &n3n, + &bwsel); + if (ret < 0) + return ret; + + hwdata->drvdata->rate_clkout0 = rate; + i = 0; + + /* Enable Free running mode */ + buf[i] = SI5324_CONTROL; + buf[i + 1] = SI5324_FREE_RUN_EN; + i += 2; + + /* Loop bandwidth */ + buf[i] = SI5324_BWSEL; + buf[i + 1] = (bwsel << SI5324_BWSEL_SHIFT) | SI5324_BWSEL_DEF_VAL; + i += 2; + + /* Enable reference clock 2 in free running mode */ + buf[i] = SI5324_POWERDOWN; + /* Enable input clock 2, Disable input clock 1 */ + buf[i + 1] = SI5324_PD_CK1_DIS; + i += 2; + + /* N1_HS */ + buf[i] = SI5324_N1_HS; + buf[i + 1] = n1_hs << SI5324_N1_HS_VAL_SHIFT; + i += 2; + + /* NC1_LS */ + buf[i] = SI5324_NC1_LS_H; + buf[i + 1] = (u8)((ncn_ls & 0x000F0000) >> 16); + buf[i + 2] = SI5324_NC1_LS_M; + buf[i + 3] = (u8)((ncn_ls & 0x0000FF00) >> 8); + buf[i + 4] = SI5324_NC1_LS_L; + buf[i + 5] = (u8)(ncn_ls & 0x000000FF); + i += 6; + + /* N2_HS and N2_LS */ + buf[i] = SI5324_N2_HS_LS_H; + buf[i + 1] = (n2_hs << SI5324_N2_HS_LS_H_VAL_SHIFT); + buf[i + 1] |= (u8)((n2_ls & 0x000F0000) >> 16); + buf[i + 2] = SI5324_N2_LS_H; + buf[i + 3] = (u8)((n2_ls & 0x0000FF00) >> 8); + buf[i + 4] = SI5324_N2_LS_L; + buf[i + 5] = (u8)(n2_ls & 0x000000FF); + i += 6; + + /* N32 (CLKIN2 or XTAL in FREERUNNING mode) */ + buf[i] = SI5324_N32_CLKIN_H; + buf[i + 2] = SI5324_N32_CLKIN_M; + buf[i + 4] = SI5324_N32_CLKIN_L; + buf[i + 1] = (u8)((n3n & 0x00070000) >> 16); + buf[i + 3] = (u8)((n3n & 0x0000FF00) >> 8); + buf[i + 5] = (u8)(n3n & 0x000000FF); + i += 6; + + /* Start calibration */ + buf[i] = SI5324_RESET_CALIB; + buf[i + 1] = SI5324_CALIB_EN; + i += 2; + + hwdata->drvdata->params.valid = 0; + rc = si5324_bulk_scatter_write(hwdata->drvdata, SI5324_OUT_REGS, buf); + + return rc; +} + +static const struct clk_ops si5324_clkout_ops = { + .prepare = si5324_clkout_prepare, + .unprepare = si5324_clkout_unprepare, + .recalc_rate = si5324_clkout_recalc_rate, + .round_rate = si5324_clkout_round_rate, + .set_rate = si5324_clkout_set_rate, +}; + +static const struct of_device_id si5324_dt_ids[] = { + { .compatible = "silabs,si5319" }, + { .compatible = "silabs,si5324" }, + { .compatible = "silabs,si5328" }, + { } +}; +MODULE_DEVICE_TABLE(of, si5324_dt_ids); + +static int si5324_dt_parse(struct i2c_client *client) +{ + struct device_node *child, *np = client->dev.of_node; + struct si5324_platform_data *pdata; + struct property *prop; + const __be32 *p; + int num = 0; + u32 val; + + if (!np) + return 0; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* + * property silabs,pll-source : <num src>, [<..>] + * allow to selectively set pll source + */ + of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) { + if (num >= 1) { + dev_err(&client->dev, + "invalid pll %d on pll-source prop\n", num); + return -EINVAL; + } + p = of_prop_next_u32(prop, p, &val); + if (!p) { + dev_err(&client->dev, + "missing pll-source for pll %d\n", num); + return -EINVAL; + } + + switch (val) { + case 0: + dev_dbg(&client->dev, "using xtal as parent for pll\n"); + pdata->pll_src = SI5324_PLL_SRC_XTAL; + break; + case 1: + dev_dbg(&client->dev, + "using clkin1 as parent for pll\n"); + pdata->pll_src = SI5324_PLL_SRC_CLKIN1; + break; + case 2: + dev_dbg(&client->dev, + "using clkin2 as parent for pll\n"); + pdata->pll_src = SI5324_PLL_SRC_CLKIN2; + break; + default: + dev_err(&client->dev, + "invalid parent %d for pll %d\n", val, num); + return -EINVAL; + } + } + /* per clkout properties */ + for_each_child_of_node(np, child) { + if (of_property_read_u32(child, "reg", &num)) { + dev_err(&client->dev, "missing reg property of %s\n", + child->name); + goto put_child; + } + + if (num >= 2) { + dev_err(&client->dev, "invalid clkout %d\n", num); + goto put_child; + } + + if (!of_property_read_u32(child, "silabs,drive-strength", + &val)) { + switch (val) { + case SI5324_DRIVE_2MA: + case SI5324_DRIVE_4MA: + case SI5324_DRIVE_6MA: + case SI5324_DRIVE_8MA: + pdata->clkout[num].drive = val; + break; + default: + dev_err(&client->dev, + "invalid drive strength %d for clkout %d\n", + val, num); + goto put_child; + } + } + + if (!of_property_read_u32(child, "clock-frequency", &val)) { + dev_dbg(&client->dev, "clock-frequency = %u\n", val); + pdata->clkout[num].rate = val; + } else { + dev_err(&client->dev, + "missing clock-frequency property of %s\n", + child->name); + goto put_child; + } + } + client->dev.platform_data = pdata; + + return 0; +put_child: + of_node_put(child); + return -EINVAL; +} + +static u8 instance; + +static int si5324_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si5324_platform_data *pdata; + struct si5324_driver_data *drvdata; + struct clk_init_data init; + struct clk *clk; + const char *parent_names[3]; + char inst_names[NUM_NAME_IDS][MAX_NAME_LEN]; + u8 num_parents, num_clocks; + int ret, n; + enum si53xx_variant variant = id->driver_data; + + if (variant > si5328) { + dev_err(&client->dev, "si53xx device not present\n"); + return -ENODEV; + } + + dev_info(&client->dev, "%s probed\n", si53xx_variant_name[variant]); + ret = si5324_dt_parse(client); + if (ret) + return ret; + + pdata = client->dev.platform_data; + if (!pdata) + return -EINVAL; + + drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->client = client; + drvdata->pxtal = devm_clk_get(&client->dev, "xtal"); + drvdata->pclkin1 = devm_clk_get(&client->dev, "clkin1"); + drvdata->pclkin2 = devm_clk_get(&client->dev, "clkin2"); + + if (PTR_ERR(drvdata->pxtal) == -EPROBE_DEFER || + PTR_ERR(drvdata->pclkin1) == -EPROBE_DEFER || + PTR_ERR(drvdata->pclkin2) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + drvdata->regmap = devm_regmap_init_i2c(client, &si5324_regmap_config); + if (IS_ERR(drvdata->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(drvdata->regmap); + } + + i2c_set_clientdata(client, drvdata); + si5324_initialize(drvdata); + + /* setup input clock configuration */ + ret = si5324_pll_reparent(drvdata, 0, pdata->pll_src); + if (ret) { + dev_err(&client->dev, + "failed to reparent pll to %d\n", + pdata->pll_src); + return ret; + } + + for (n = 0; n < SI5324_MAX_CLKOUTS; n++) { + ret = si5324_clkout_set_drive_strength(drvdata, n, + pdata->clkout[n].drive); + if (ret) { + dev_err(&client->dev, + "failed set drive strength of clkout%d to %d\n", + n, pdata->clkout[n].drive); + return ret; + } + } + + if (!IS_ERR(drvdata->pxtal)) + clk_prepare_enable(drvdata->pxtal); + if (!IS_ERR(drvdata->pclkin1)) + clk_prepare_enable(drvdata->pclkin1); + if (!IS_ERR(drvdata->pclkin2)) + clk_prepare_enable(drvdata->pclkin2); + + /* create instance names by appending instance id */ + for (n = 0; n < SI5324_SRC_CLKS; n++) { + sprintf(inst_names[n], "%s_%d", si5324_input_names[n], + instance); + } + sprintf(inst_names[3], "%s_%d", si5324_pll_name, instance); + for (n = 0; n < SI5324_MAX_CLKOUTS; n++) { + sprintf(inst_names[n + 4], "%s_%d", si5324_clkout_names[n], + instance); + } + + /* register xtal input clock gate */ + memset(&init, 0, sizeof(init)); + init.name = inst_names[0]; + init.ops = &si5324_xtal_ops; + init.flags = 0; + + if (!IS_ERR(drvdata->pxtal)) { + drvdata->pxtal_name = __clk_get_name(drvdata->pxtal); + init.parent_names = &drvdata->pxtal_name; + init.num_parents = 1; + } + drvdata->xtal.init = &init; + + clk = devm_clk_register(&client->dev, &drvdata->xtal); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + ret = PTR_ERR(clk); + goto err_clk; + } + + /* register clkin1 input clock gate */ + memset(&init, 0, sizeof(init)); + init.name = inst_names[1]; + init.ops = &si5324_clkin_ops; + if (!IS_ERR(drvdata->pclkin1)) { + drvdata->pclkin1_name = __clk_get_name(drvdata->pclkin1); + init.parent_names = &drvdata->pclkin1_name; + init.num_parents = 1; + } + + drvdata->clkin1.init = &init; + clk = devm_clk_register(&client->dev, &drvdata->clkin1); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + ret = PTR_ERR(clk); + goto err_clk; + } + + /* register clkin2 input clock gate */ + memset(&init, 0, sizeof(init)); + init.name = inst_names[2]; + init.ops = &si5324_clkin_ops; + if (!IS_ERR(drvdata->pclkin2)) { + drvdata->pclkin2_name = __clk_get_name(drvdata->pclkin2); + init.parent_names = &drvdata->pclkin2_name; + init.num_parents = 1; + } + + drvdata->clkin2.init = &init; + clk = devm_clk_register(&client->dev, &drvdata->clkin2); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + ret = PTR_ERR(clk); + goto err_clk; + } + + /* Si5324 allows to mux xtal or clkin1 or clkin2 to PLL input */ + num_parents = SI5324_SRC_CLKS; + parent_names[0] = inst_names[0]; + parent_names[1] = inst_names[1]; + parent_names[2] = inst_names[2]; + + /* register PLL */ + drvdata->pll.drvdata = drvdata; + drvdata->pll.hw.init = &init; + memset(&init, 0, sizeof(init)); + init.name = inst_names[3]; + init.ops = &si5324_pll_ops; + init.flags = 0; + init.flags |= CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE; + init.parent_names = parent_names; + init.num_parents = num_parents; + + clk = devm_clk_register(&client->dev, &drvdata->pll.hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", init.name); + ret = PTR_ERR(clk); + goto err_clk; + } + + /* register clk out divider */ + num_clocks = 2; + num_parents = 1; + parent_names[0] = inst_names[3]; + + drvdata->clkout = devm_kzalloc(&client->dev, num_clocks * + sizeof(*drvdata->clkout), GFP_KERNEL); + + drvdata->onecell.clk_num = num_clocks; + drvdata->onecell.clks = devm_kzalloc(&client->dev, + num_clocks * + sizeof(*drvdata->onecell.clks), + GFP_KERNEL); + + if (WARN_ON(!drvdata->clkout) || !drvdata->onecell.clks) { + ret = -ENOMEM; + goto err_clk; + } + + for (n = 0; n < num_clocks; n++) { + drvdata->clkout[n].num = n; + drvdata->clkout[n].drvdata = drvdata; + drvdata->clkout[n].hw.init = &init; + memset(&init, 0, sizeof(init)); + init.name = inst_names[4 + n]; + init.ops = &si5324_clkout_ops; + init.flags = 0; + init.flags |= CLK_SET_RATE_PARENT; + init.parent_names = parent_names; + init.num_parents = num_parents; + + clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + ret = PTR_ERR(clk); + goto err_clk; + } + /* refer to output clock in onecell */ + drvdata->onecell.clks[n] = clk; + + /* set initial clkout rate */ + if (pdata->clkout[n].rate != 0) { + int ret; + + ret = clk_set_rate(clk, pdata->clkout[n].rate); + if (ret != 0) { + dev_err(&client->dev, "Cannot set rate : %d\n", + ret); + } + } + } + + ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, + &drvdata->onecell); + if (ret) { + dev_err(&client->dev, "unable to add clk provider\n"); + goto err_clk; + } + + dev_info(&client->dev, "%s probe successful\n", + si53xx_variant_name[variant]); + instance++; + return 0; + +err_clk: + if (!IS_ERR(drvdata->pxtal)) + clk_disable_unprepare(drvdata->pxtal); + if (!IS_ERR(drvdata->pclkin1)) + clk_disable_unprepare(drvdata->pclkin1); + if (!IS_ERR(drvdata->pclkin2)) + clk_disable_unprepare(drvdata->pclkin2); + + return ret; +} + +static int si5324_i2c_remove(struct i2c_client *client) +{ + of_clk_del_provider(client->dev.of_node); + return 0; +} + +static const struct i2c_device_id si5324_i2c_ids[] = { + { "si5319", si5319 }, + { "si5324", si5324 }, + { "si5328", si5328 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si5324_i2c_ids); + +static struct i2c_driver si5324_driver = { + .driver = { + .name = "si5324", + .of_match_table = of_match_ptr(si5324_dt_ids), + }, + .probe = si5324_i2c_probe, + .remove = si5324_i2c_remove, + .id_table = si5324_i2c_ids, +}; +module_i2c_driver(si5324_driver); + +MODULE_AUTHOR("Venkateshwar Rao G <vgannava@xilinx.com>"); +MODULE_DESCRIPTION("Silicon Labs 5319/5324/5328 clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/clk-si5324.h b/drivers/clk/clk-si5324.h new file mode 100644 index 000000000000..48e62a67f56e --- /dev/null +++ b/drivers/clk/clk-si5324.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Si5324 clock driver + * + * Copyright (C) 2017-2018 Xilinx, Inc. + * + * Authors: Leon Woestenberg <leon@sidebranch.com> + * Venkateshwar Rao <vgannava@xilinx.com> + */ + +#ifndef _CLK_SI5324_H_ +#define _CLK_SI5324_H_ + +#define SI5324_BUS_BASE_ADDR 0x68 + +#define SI5324_CONTROL 0 +#define SI5324_CONTROL_FREE_RUN BIT(6) +#define SI5324_FREE_RUN_EN 0x54 + +#define SI5324_INCK_PRIOR 1 +#define SI5324_INCK_PRIOR_1_MASK 0xC +#define SI5324_INCK_PRIOI_2_MASK 0x3 + +#define SI5324_BWSEL 2 +#define SI5324_BWSEL_MASK 0xF0 +#define SI5324_BWSEL_SHIFT 4 +#define SI5324_BWSEL_DEF_VAL 2 + +#define SI5324_CKSEL 3 +#define SI5324_CKSEL_SQL_ICAL BIT(4) +#define SI5324_CKSEL_SHIFT 6 +#define SI5324_CK_SEL 3 + +#define SI3324_AUTOSEL 4 +#define SI5324_AUTOSEL_DEF 0x12 + +#define SI5324_ICMOS 5 +#define SI5324_OUTPUT_SIGFMT 6 +#define SI5324_OUTPUT_SF1_DEFAULT 0xF +#define SI5324_REFFRE_FOS 7 +#define SI5324_HLOG 8 +#define SI5324_AVG_HIST 9 +#define SI5324_DSBL_CLKOUT 10 +#define SI5324_DSBL_CLKOUT2 BIT(3) +#define SI5324_POWERDOWN 11 +#define SI5324_PD_CK1 BIT(0) +#define SI5324_PD_CK2 BIT(1) +#define SI5324_PD_CK1_DIS 0x41 +#define SI5324_PD_CK2_DIS 0x42 +#define SI5324_FOS_LOCKT 19 +#define SI5324_FOS_DEFAULT 0x23 +#define SI5324_CK_ACTV_SEL 21 +#define SI5324_CK_DEFAULT 0xFC +#define SI5324_CK_ACTV BIT(1) +#define SI5324_CK_SELPIN BIT(1) +#define SI5324_LOS_MSK 23 +#define SI5324_FOS_L0L_MASK 24 + +/* output clock dividers */ +#define SI5324_N1_HS 25 +#define SI5324_N1_HS_VAL_SHIFT 5 +#define SI5324_HSHIFT 16 +#define SI5324_LSHIFT 8 +#define SI5324_NC1_LS_H 31 +#define SI5324_NC1_LS_M 32 +#define SI5324_NC1_LS_L 33 +#define SI5324_DIV_LS_MASK 0x0F +#define SI5324_DIV_HS_MASK 0xF0 +#define SI5324_NC2_LS_H 34 +#define SI5324_NC2_LS_M 35 +#define SI5324_NC2_LS_L 36 + +#define SI5324_N2_HS_LS_H 40 +#define SI5324_N2_HS_LS_H_VAL_SHIFT 5 +#define SI5324_N2_LS_H 41 +#define SI5324_N2_LS_L 42 +#define SI5324_N31_CLKIN_H 43 +#define SI5324_N31_CLKIN_M 44 +#define SI5324_N31_CLKIN_L 45 +#define SI5324_N32_CLKIN_H 46 +#define SI5324_N32_CLKIN_M 47 +#define SI5324_N32_CLKIN_L 48 +#define SI5324_FOS_CLKIN_RATE 55 +#define SI5324_PLL_ACTV_CLK 128 +#define SI5324_LOS_STATUS 129 +#define SI5324_CLKIN_LOL_STATUS 130 +#define SI5324_LOS_FLG 131 +#define SI5324_FOS_FLG 132 +#define SI5324_PARTNO_H 134 +#define SI5324_PARTNO_L 135 + +#define SI5324_RESET_CALIB 136 +#define SI5324_RST_ALL BIT(7) +#define SI5324_CALIB_EN BIT(6) + +#define SI5324_FASTLOCK 137 +#define SI5324_FASTLOCK_EN BIT(0) +#define SI5324_LOS1_LOS2_EN 138 +#define SI5324_SKEW1 142 +#define SI5324_SKEW2 143 + +/* selects 2kHz to 710 MHz */ +#define SI5324_CLKIN_MIN_FREQ 2000 +#define SI5324_CLKIN_MAX_FREQ (710 * 1000 * 1000) + +/* generates 2kHz to 945 MHz */ +#define SI5324_CLKOUT_MIN_FREQ 2000 +#define SI5324_CLKOUT_MAX_FREQ (945 * 1000 * 1000) + +/* The following constants define the limits of the divider settings. */ +#define SI5324_N1_HS_MIN 6 +#define SI5324_N1_HS_MAX 11 +#define SI5324_NC_LS_MIN 1 +#define SI5324_NC_LS_MAX 0x100000 +#define SI5324_N2_HS_MIN 4 +#define SI5324_N2_HS_MAX 11 +#define SI5324_N2_LS_MIN 2 +#define SI5324_N2_LS_MAX 0x100000 +#define SI5324_N3_MIN 1 +#define SI5324_N3_MAX 0x080000 + +#define SI5324_SRC_XTAL 0 +#define SI5324_SRC_CLKIN1 1 +#define SI5324_SRC_CLKIN2 2 +#define SI5324_SRC_CLKS 3 + +#define SI5324_CLKIN1 0 +#define SI5324_CLKIN2 1 +#define SI5324_MAX_CLKOUTS 2 +#define NUM_NAME_IDS 6 /* 3 clkin, 1 pll, 2 clkout */ +#define MAX_NAME_LEN 11 +#define SI5324_PARAM_LEN 24 +#define SI5324_NC_PARAM_LEN 6 +#define SI5324_OUT_REGS 14 +#define SI5324_N1_PARAM_LEN 1 +#define SI5324_N2_PARAM_LEN 9 +#define SI5324_REF_CLOCK 114285000UL +#define SI5324_RESET_DELAY_MS 20 + +#endif diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index 07ef9995b3cb..31504e52a67c 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -732,10 +732,8 @@ static int si5341_output_clk_set_rate(struct clk_hw *hw, unsigned long rate, r[0] = r_div ? (r_div & 0xff) : 1; r[1] = (r_div >> 8) & 0xff; r[2] = (r_div >> 16) & 0xff; - err = regmap_bulk_write(output->data->regmap, + return regmap_bulk_write(output->data->regmap, SI5341_OUT_R_REG(output), r, 3); - - return 0; } static int si5341_output_reparent(struct clk_si5341_output *output, u8 index) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 13332f89e034..4570000acf59 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -37,7 +37,11 @@ static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); -static struct hlist_head *all_lists[] = { +/* List of registered clks that use runtime PM */ +static HLIST_HEAD(clk_rpm_list); +static DEFINE_MUTEX(clk_rpm_list_lock); + +static const struct hlist_head *all_lists[] = { &clk_root_list, &clk_orphan_list, NULL, @@ -59,6 +63,7 @@ struct clk_core { struct clk_hw *hw; struct module *owner; struct device *dev; + struct hlist_node rpm_node; struct device_node *of_node; struct clk_core *parent; struct clk_parent_map *parents; @@ -129,6 +134,89 @@ static void clk_pm_runtime_put(struct clk_core *core) pm_runtime_put_sync(core->dev); } +/** + * clk_pm_runtime_get_all() - Runtime "get" all clk provider devices + * + * Call clk_pm_runtime_get() on all runtime PM enabled clks in the clk tree so + * that disabling unused clks avoids a deadlock where a device is runtime PM + * resuming/suspending and the runtime PM callback is trying to grab the + * prepare_lock for something like clk_prepare_enable() while + * clk_disable_unused_subtree() holds the prepare_lock and is trying to runtime + * PM resume/suspend the device as well. + * + * Context: Acquires the 'clk_rpm_list_lock' and returns with the lock held on + * success. Otherwise the lock is released on failure. + * + * Return: 0 on success, negative errno otherwise. + */ +static int clk_pm_runtime_get_all(void) +{ + int ret; + struct clk_core *core, *failed; + + /* + * Grab the list lock to prevent any new clks from being registered + * or unregistered until clk_pm_runtime_put_all(). + */ + mutex_lock(&clk_rpm_list_lock); + + /* + * Runtime PM "get" all the devices that are needed for the clks + * currently registered. Do this without holding the prepare_lock, to + * avoid the deadlock. + */ + hlist_for_each_entry(core, &clk_rpm_list, rpm_node) { + ret = clk_pm_runtime_get(core); + if (ret) { + failed = core; + pr_err("clk: Failed to runtime PM get '%s' for clk '%s'\n", + dev_name(failed->dev), failed->name); + goto err; + } + } + + return 0; + +err: + hlist_for_each_entry(core, &clk_rpm_list, rpm_node) { + if (core == failed) + break; + + clk_pm_runtime_put(core); + } + mutex_unlock(&clk_rpm_list_lock); + + return ret; +} + +/** + * clk_pm_runtime_put_all() - Runtime "put" all clk provider devices + * + * Put the runtime PM references taken in clk_pm_runtime_get_all() and release + * the 'clk_rpm_list_lock'. + */ +static void clk_pm_runtime_put_all(void) +{ + struct clk_core *core; + + hlist_for_each_entry(core, &clk_rpm_list, rpm_node) + clk_pm_runtime_put(core); + mutex_unlock(&clk_rpm_list_lock); +} + +static void clk_pm_runtime_init(struct clk_core *core) +{ + struct device *dev = core->dev; + + if (dev && pm_runtime_enabled(dev)) { + core->rpm_enabled = true; + + mutex_lock(&clk_rpm_list_lock); + hlist_add_head(&core->rpm_node, &clk_rpm_list); + mutex_unlock(&clk_rpm_list_lock); + } +} + /*** locking ***/ static void clk_prepare_lock(void) { @@ -251,6 +339,17 @@ static bool clk_core_is_enabled(struct clk_core *core) } } + /* + * This could be called with the enable lock held, or from atomic + * context. If the parent isn't enabled already, we can't do + * anything here. We can also assume this clock isn't enabled. + */ + if ((core->flags & CLK_OPS_PARENT_ENABLE) && core->parent) + if (!clk_core_is_enabled(core->parent)) { + ret = false; + goto done; + } + ret = core->ops->is_enabled(core->hw); done: if (core->rpm_enabled) @@ -291,6 +390,34 @@ struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw) } EXPORT_SYMBOL_GPL(clk_hw_get_parent); +static unsigned int sibling; + +static void clk_show_subtree(struct clk_core *c, + int level) +{ + struct clk_core *child; + + if (!c) + return; + + if (level == 1) + sibling++; + + hlist_for_each_entry(child, &c->children, child_node) + clk_show_subtree(child, level + 1); +} + +unsigned int clk_get_children(char *name) +{ + struct clk_core *core; + struct clk *pclk = __clk_lookup(name); + sibling = 0; + + core = pclk->core; + clk_show_subtree(core, 0); + return sibling; +} + static struct clk_core *__clk_lookup_subtree(const char *name, struct clk_core *core) { @@ -414,6 +541,9 @@ static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index) if (IS_ERR(hw)) return ERR_CAST(hw); + if (!hw) + return NULL; + return hw->core; } @@ -852,10 +982,9 @@ static void clk_core_unprepare(struct clk_core *core) if (core->ops->unprepare) core->ops->unprepare(core->hw); - clk_pm_runtime_put(core); - trace_clk_unprepare_complete(core); clk_core_unprepare(core->parent); + clk_pm_runtime_put(core); } static void clk_core_unprepare_lock(struct clk_core *core) @@ -1224,9 +1353,6 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) if (core->flags & CLK_IGNORE_UNUSED) return; - if (clk_pm_runtime_get(core)) - return; - if (clk_core_is_prepared(core)) { trace_clk_unprepare(core); if (core->ops->unprepare_unused) @@ -1235,8 +1361,6 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) core->ops->unprepare(core->hw); trace_clk_unprepare_complete(core); } - - clk_pm_runtime_put(core); } static void clk_disable_unused_subtree(struct clk_core *core) @@ -1252,9 +1376,6 @@ static void clk_disable_unused_subtree(struct clk_core *core) if (core->flags & CLK_OPS_PARENT_ENABLE) clk_core_prepare_enable(core->parent); - if (clk_pm_runtime_get(core)) - goto unprepare_out; - flags = clk_enable_lock(); if (core->enable_count) @@ -1279,8 +1400,6 @@ static void clk_disable_unused_subtree(struct clk_core *core) unlock_out: clk_enable_unlock(flags); - clk_pm_runtime_put(core); -unprepare_out: if (core->flags & CLK_OPS_PARENT_ENABLE) clk_core_disable_unprepare(core->parent); } @@ -1296,12 +1415,22 @@ __setup("clk_ignore_unused", clk_ignore_unused_setup); static int clk_disable_unused(void) { struct clk_core *core; + int ret; if (clk_ignore_unused) { pr_warn("clk: Not disabling unused clocks\n"); return 0; } + pr_info("clk: Disabling unused clocks\n"); + + ret = clk_pm_runtime_get_all(); + if (ret) + return ret; + /* + * Grab the prepare lock to keep the clk topology stable while iterating + * over clks. + */ clk_prepare_lock(); hlist_for_each_entry(core, &clk_root_list, child_node) @@ -1318,6 +1447,8 @@ static int clk_disable_unused(void) clk_prepare_unlock(); + clk_pm_runtime_put_all(); + return 0; } late_initcall_sync(clk_disable_unused); @@ -3070,6 +3201,7 @@ static void possible_parent_show(struct seq_file *s, struct clk_core *core, unsigned int i, char terminator) { struct clk_core *parent; + const char *name = NULL; /* * Go through the following options to fetch a parent's name. @@ -3084,18 +3216,20 @@ static void possible_parent_show(struct seq_file *s, struct clk_core *core, * registered (yet). */ parent = clk_core_get_parent_by_index(core, i); - if (parent) + if (parent) { seq_puts(s, parent->name); - else if (core->parents[i].name) + } else if (core->parents[i].name) { seq_puts(s, core->parents[i].name); - else if (core->parents[i].fw_name) + } else if (core->parents[i].fw_name) { seq_printf(s, "<%s>(fw)", core->parents[i].fw_name); - else if (core->parents[i].index >= 0) - seq_puts(s, - of_clk_get_parent_name(core->of_node, - core->parents[i].index)); - else - seq_puts(s, "(missing)"); + } else { + if (core->parents[i].index >= 0) + name = of_clk_get_parent_name(core->of_node, core->parents[i].index); + if (!name) + name = "(missing)"; + + seq_puts(s, name); + } seq_putc(s, terminator); } @@ -3491,9 +3625,6 @@ static int __clk_core_init(struct clk_core *core) } clk_core_reparent_orphans_nolock(); - - - kref_init(&core->ref); out: clk_pm_runtime_put(core); unlock: @@ -3703,6 +3834,22 @@ static void clk_core_free_parent_map(struct clk_core *core) kfree(core->parents); } +/* Free memory allocated for a struct clk_core */ +static void __clk_release(struct kref *ref) +{ + struct clk_core *core = container_of(ref, struct clk_core, ref); + + if (core->rpm_enabled) { + mutex_lock(&clk_rpm_list_lock); + hlist_del(&core->rpm_node); + mutex_unlock(&clk_rpm_list_lock); + } + + clk_core_free_parent_map(core); + kfree_const(core->name); + kfree(core); +} + static struct clk * __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw) { @@ -3723,6 +3870,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw) goto fail_out; } + kref_init(&core->ref); + core->name = kstrdup_const(init->name, GFP_KERNEL); if (!core->name) { ret = -ENOMEM; @@ -3735,9 +3884,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw) } core->ops = init->ops; - if (dev && pm_runtime_enabled(dev)) - core->rpm_enabled = true; core->dev = dev; + clk_pm_runtime_init(core); core->of_node = np; if (dev && dev->driver) core->owner = dev->driver->owner; @@ -3777,12 +3925,10 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw) hw->clk = NULL; fail_create_clk: - clk_core_free_parent_map(core); fail_parents: fail_ops: - kfree_const(core->name); fail_name: - kfree(core); + kref_put(&core->ref, __clk_release); fail_out: return ERR_PTR(ret); } @@ -3862,18 +4008,6 @@ int of_clk_hw_register(struct device_node *node, struct clk_hw *hw) } EXPORT_SYMBOL_GPL(of_clk_hw_register); -/* Free memory allocated for a clock. */ -static void __clk_release(struct kref *ref) -{ - struct clk_core *core = container_of(ref, struct clk_core, ref); - - lockdep_assert_held(&prepare_lock); - - clk_core_free_parent_map(core); - kfree_const(core->name); - kfree(core); -} - /* * Empty clk_ops for unregistered clocks. These are used temporarily * after clk_unregister() was called on a clock and until last clock @@ -3926,7 +4060,7 @@ static void clk_core_evict_parent_cache_subtree(struct clk_core *root, /* Remove this clk from all parent caches */ static void clk_core_evict_parent_cache(struct clk_core *core) { - struct hlist_head **lists; + const struct hlist_head **lists; struct clk_core *root; lockdep_assert_held(&prepare_lock); diff --git a/drivers/clk/hisilicon/clk-hi3519.c b/drivers/clk/hisilicon/clk-hi3519.c index ad0c7f350cf0..60d8a27a9082 100644 --- a/drivers/clk/hisilicon/clk-hi3519.c +++ b/drivers/clk/hisilicon/clk-hi3519.c @@ -130,7 +130,7 @@ static void hi3519_clk_unregister(struct platform_device *pdev) of_clk_del_provider(pdev->dev.of_node); hisi_clk_unregister_gate(hi3519_gate_clks, - ARRAY_SIZE(hi3519_mux_clks), + ARRAY_SIZE(hi3519_gate_clks), crg->clk_data); hisi_clk_unregister_mux(hi3519_mux_clks, ARRAY_SIZE(hi3519_mux_clks), diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index a3d04c7c3da8..eb9c139babc3 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -467,8 +467,10 @@ static void __init hi3620_mmc_clk_init(struct device_node *node) return; clk_data->clks = kcalloc(num, sizeof(*clk_data->clks), GFP_KERNEL); - if (!clk_data->clks) + if (!clk_data->clks) { + kfree(clk_data); return; + } for (i = 0; i < num; i++) { struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i]; diff --git a/drivers/clk/idt/Makefile b/drivers/clk/idt/Makefile new file mode 100644 index 000000000000..4cf2b6e4801d --- /dev/null +++ b/drivers/clk/idt/Makefile @@ -0,0 +1,3 @@ +obj-y += clk-idt8t49n24x-core.o +obj-y += clk-idt8t49n24x-debugfs.o +obj-y += clk-idt8t49n24x.o diff --git a/drivers/clk/idt/clk-idt8t49n24x-core.c b/drivers/clk/idt/clk-idt8t49n24x-core.c new file mode 100644 index 000000000000..ad23014e708f --- /dev/null +++ b/drivers/clk/idt/clk-idt8t49n24x-core.c @@ -0,0 +1,933 @@ +// SPDX-License-Identifier: GPL-2.0 +/* clk-idt8t49n24x-core.c - Program 8T49N24x settings via I2C (common code) + * + * Copyright (C) 2018, Integrated Device Technology, Inc. <david.cater@idt.com> + * + * See https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html + * This program is distributed "AS IS" and WITHOUT ANY WARRANTY; + * including the implied warranties of MERCHANTABILITY, FITNESS FOR + * A PARTICULAR PURPOSE, or NON-INFRINGEMENT. + */ + +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include "clk-idt8t49n24x-core.h" + +/* + * In Timing Commander, Q0 is changed from 25MHz to Q0 75MHz, the following + * changes occur: + * + * 2 bytes change in EEPROM data string. + * + * DSM_INT R0025[0],R0026[7:0] : 35 => 30 + * NS2_Q0 R0040[7:0],R0041[7:0] : 14 => 4 + * + * In EEPROM + * 1. R0026 + * 2. R0041 + * + * Note that VCO_Frequency (metadata) also changed (3500 =>3000). + * This reflects a change to DSM_INT. + * + * Note that the Timing Commander code has workarounds in the workflow scripts + * to handle dividers for the 8T49N241 (because the development of that GUI + * predates chip override functionality). That affects NS1_Qx (x in 1-3) + * and NS2_Qx. NS1_Qx contains the upper bits of NS_Qx, and NS2_Qx contains + * the lower bits. That is NOT the case for Q0, though. In that case NS1_Q0 + * is the 1st stage output divider (/5, /6, /4) and NS2_Q0 is the 16-bit + * second stage (with actual divide being twice the value stored in the + * register). + * + * NS1_Q0 R003F[1:0] + */ + +#define IDT24x_VCO_MIN 2999997000u +#define IDT24x_VCO_MAX 4000004000u +#define IDT24x_VCO_OPT 3500000000u +#define IDT24x_MIN_INT_DIVIDER 6 +#define IDT24x_MIN_NS1 4 +#define IDT24x_MAX_NS1 6 + +static u8 q0_ns1_options[3] = { 5, 6, 4 }; + +/** + * bits_to_shift - num bits to shift given specified mask + * @mask: 32-bit word input to count zero bits on right + * + * Given a bit mask indicating where a value will be stored in + * a register, return the number of bits you need to shift the value + * before ORing it into the register value. + * + * Return: number of bits to shift + */ +int bits_to_shift(unsigned int mask) +{ + /* the number of zero bits on the right */ + unsigned int c = 32; + + mask &= ~mask + 1; + if (mask) + c--; + if (mask & 0x0000FFFF) + c -= 16; + if (mask & 0x00FF00FF) + c -= 8; + if (mask & 0x0F0F0F0F) + c -= 4; + if (mask & 0x33333333) + c -= 2; + if (mask & 0x55555555) + c -= 1; + return c; +} + +/* + * TODO: Consider replacing this with regmap_multi_reg_write, which + * supports introducing a delay after each write. Experiment to see if + * the writes succeed consistently when using that API. + */ +static int regmap_bulk_write_with_retry( + struct regmap *map, unsigned int offset, u8 val[], + int val_count, int max_attempts) +{ + int err = 0; + int count = 1; + + do { + err = regmap_bulk_write(map, offset, val, val_count); + if (err == 0) + return 0; + + usleep_range(100, 200); + } while (count++ <= max_attempts); + return err; +} + +static int regmap_write_with_retry( + struct regmap *map, unsigned int offset, unsigned int val, + int max_attempts) +{ + int err = 0; + int count = 1; + + do { + err = regmap_write(map, offset, val); + if (err == 0) + return 0; + usleep_range(100, 200); + } while (count++ <= max_attempts); + return err; +} + +/* + * TODO: Consider using regmap_multi_reg_write instead. Explore + * use of regmap to configure WRITE_BLOCK_SIZE, and using the delay + * mechanism in regmap_multi_reg_write instead of retrying multiple + * times (regmap_bulk_write_with_retry). + */ +int i2cwritebulk( + struct i2c_client *client, struct regmap *map, + unsigned int reg, u8 val[], size_t val_count) +{ + char dbg[128]; + u8 block[WRITE_BLOCK_SIZE]; + unsigned int block_offset = reg; + int x; + int err = 0; + int currentOffset = 0; + + dev_dbg(&client->dev, "I2C->0x%04x : [hex] . First byte: %02x, Second byte: %02x", + reg, reg >> 8, reg & 0xFF); + dbg[0] = 0; + + for (x = 0; x < val_count; x++) { + char data[4]; + + block[currentOffset++] = val[x]; + sprintf(data, "%02x ", val[x]); + strcat(dbg, data); + if (x > 0 && (x + 1) % WRITE_BLOCK_SIZE == 0) { + dev_dbg(&client->dev, "%s", dbg); + dbg[0] = '\0'; + sprintf(dbg, + "(loop) calling regmap_bulk_write @ 0x%04x [%d bytes]", + block_offset, WRITE_BLOCK_SIZE); + dev_dbg(&client->dev, "%s", dbg); + dbg[0] = '\0'; + err = regmap_bulk_write_with_retry( + map, block_offset, block, WRITE_BLOCK_SIZE, 5); + if (err != 0) + break; + block_offset += WRITE_BLOCK_SIZE; + currentOffset = 0; + } + } + if (err == 0 && currentOffset > 0) { + dev_dbg(&client->dev, "%s", dbg); + dev_dbg(&client->dev, "(final) calling regmap_bulk_write @ 0x%04x [%d bytes]", + block_offset, currentOffset); + err = regmap_bulk_write_with_retry( + map, block_offset, block, currentOffset, 5); + } + + return err; +} + +static int i2cwrite( + struct i2c_client *client, struct regmap *map, + unsigned int reg, unsigned int val) +{ + int err; + + dev_dbg(&client->dev, "I2C->0x%x : [hex] %x", reg, val); + err = regmap_write_with_retry(map, reg, val, 5); + usleep_range(100, 200); + return err; +} + +static int i2cwritewithmask( + struct i2c_client *client, struct regmap *map, unsigned int reg, + u8 val, u8 original, u8 mask) +{ + return i2cwrite(client, map, reg, + ((val << bits_to_shift(mask)) & mask) | (original & ~mask)); +} + +int idt24x_get_offsets( + u8 output_num, + struct clk_register_offsets *offsets) +{ + switch (output_num) { + case 0: + offsets->oe_offset = IDT24x_REG_OUTEN; + offsets->oe_mask = IDT24x_REG_OUTEN0_MASK; + offsets->dis_mask = IDT24x_REG_Q0_DIS_MASK; + offsets->ns1_offset = IDT24x_REG_NS1_Q0; + offsets->ns1_offset_mask = IDT24x_REG_NS1_Q0_MASK; + offsets->ns2_15_8_offset = IDT24x_REG_NS2_Q0_15_8; + offsets->ns2_7_0_offset = IDT24x_REG_NS2_Q0_7_0; + break; + case 1: + offsets->oe_offset = IDT24x_REG_OUTEN; + offsets->oe_mask = IDT24x_REG_OUTEN1_MASK; + offsets->dis_mask = IDT24x_REG_Q1_DIS_MASK; + offsets->n_17_16_offset = IDT24x_REG_N_Q1_17_16; + offsets->n_17_16_mask = IDT24x_REG_N_Q1_17_16_MASK; + offsets->n_15_8_offset = IDT24x_REG_N_Q1_15_8; + offsets->n_7_0_offset = IDT24x_REG_N_Q1_7_0; + offsets->nfrac_27_24_offset = IDT24x_REG_NFRAC_Q1_27_24; + offsets->nfrac_27_24_mask = + IDT24x_REG_NFRAC_Q1_27_24_MASK; + offsets->nfrac_23_16_offset = IDT24x_REG_NFRAC_Q1_23_16; + offsets->nfrac_15_8_offset = IDT24x_REG_NFRAC_Q1_15_8; + offsets->nfrac_7_0_offset = IDT24x_REG_NFRAC_Q1_7_0; + break; + case 2: + offsets->oe_offset = IDT24x_REG_OUTEN; + offsets->oe_mask = IDT24x_REG_OUTEN2_MASK; + offsets->dis_mask = IDT24x_REG_Q2_DIS_MASK; + offsets->n_17_16_offset = IDT24x_REG_N_Q2_17_16; + offsets->n_17_16_mask = IDT24x_REG_N_Q2_17_16_MASK; + offsets->n_15_8_offset = IDT24x_REG_N_Q2_15_8; + offsets->n_7_0_offset = IDT24x_REG_N_Q2_7_0; + offsets->nfrac_27_24_offset = IDT24x_REG_NFRAC_Q2_27_24; + offsets->nfrac_27_24_mask = + IDT24x_REG_NFRAC_Q2_27_24_MASK; + offsets->nfrac_23_16_offset = IDT24x_REG_NFRAC_Q2_23_16; + offsets->nfrac_15_8_offset = IDT24x_REG_NFRAC_Q2_15_8; + offsets->nfrac_7_0_offset = IDT24x_REG_NFRAC_Q2_7_0; + break; + case 3: + offsets->oe_offset = IDT24x_REG_OUTEN; + offsets->oe_mask = IDT24x_REG_OUTEN3_MASK; + offsets->dis_mask = IDT24x_REG_Q3_DIS_MASK; + offsets->n_17_16_offset = IDT24x_REG_N_Q3_17_16; + offsets->n_17_16_mask = IDT24x_REG_N_Q3_17_16_MASK; + offsets->n_15_8_offset = IDT24x_REG_N_Q3_15_8; + offsets->n_7_0_offset = IDT24x_REG_N_Q3_7_0; + offsets->nfrac_27_24_offset = IDT24x_REG_NFRAC_Q3_27_24; + offsets->nfrac_27_24_mask = + IDT24x_REG_NFRAC_Q3_27_24_MASK; + offsets->nfrac_23_16_offset = IDT24x_REG_NFRAC_Q3_23_16; + offsets->nfrac_15_8_offset = IDT24x_REG_NFRAC_Q3_15_8; + offsets->nfrac_7_0_offset = IDT24x_REG_NFRAC_Q3_7_0; + break; + default: + return -EINVAL; + } + return 0; +} + +/** + * idt24x_calc_div_q0 - Calculate dividers and VCO freq to generate + * the specified Q0 frequency. + * @chip: Device data structure. contains all requested frequencies + * for all outputs. + * + * The actual output divider is ns1 * ns2 * 2. fOutput = fVCO / (ns1 * ns2 * 2) + * + * The options for ns1 (when the source is the VCO) are 4,5,6. ns2 is a + * 16-bit value. + * + * chip->divs: structure for specifying ns1/ns2 values. If 0 after this + * function, Q0 is not requested + * + * Return: 0 on success, negative errno otherwise. + */ +static int idt24x_calc_div_q0(struct clk_idt24x_chip *chip) +{ + u8 x; + u32 min_div, max_div, best_vco = 0; + u16 min_ns2, max_ns2; + bool is_lower_vco = false; + + chip->divs.ns1_q0 = 0; + chip->divs.ns2_q0 = 0; + + if (chip->clk[0].requested == 0) + return 0; + + min_div = div64_u64( + (u64)IDT24x_VCO_MIN, chip->clk[0].requested * 2) * 2; + max_div = div64_u64( + (u64)IDT24x_VCO_MAX, chip->clk[0].requested * 2) * 2; + + dev_dbg(&chip->i2c_client->dev, + "%s. requested: %u, min_div: %u, max_div: %u", + __func__, chip->clk[0].requested, min_div, max_div); + + min_ns2 = div64_u64((u64)min_div, IDT24x_MAX_NS1 * 2); + max_ns2 = div64_u64((u64)max_div, IDT24x_MIN_NS1 * 2); + + dev_dbg(&chip->i2c_client->dev, + "%s. min_ns2: %u, max_ns2: %u", __func__, min_ns2, max_ns2); + + for (x = 0; x < ARRAY_SIZE(q0_ns1_options); x++) { + u16 y = min_ns2; + + while (y <= max_ns2) { + u32 actual_div = q0_ns1_options[x] * y * 2; + u32 current_vco = actual_div * + chip->clk[0].requested; + + if (current_vco < IDT24x_VCO_MIN) + dev_dbg(&chip->i2c_client->dev, + "%s. ignore div: (ns1=%u * ns2=%u * 2 * %u) == %u < %u", + __func__, q0_ns1_options[x], y, + chip->clk[0].requested, + current_vco, IDT24x_VCO_MIN); + else if (current_vco > IDT24x_VCO_MAX) { + dev_dbg(&chip->i2c_client->dev, + "%s. ignore div: (ns1=%u * ns2=%u * 2 * %u) == %u > %u. EXIT LOOP.", + __func__, q0_ns1_options[x], y, + chip->clk[0].requested, + current_vco, IDT24x_VCO_MAX); + y = max_ns2; + } else { + bool use = false; + + dev_dbg(&chip->i2c_client->dev, + "%s. contender: (ns1=%u * ns2=%u * 2 * %u) == %u [in range]", + __func__, q0_ns1_options[x], y, + chip->clk[0].requested, + current_vco); + if (current_vco <= IDT24x_VCO_OPT) { + if (current_vco > best_vco || + !is_lower_vco) { + is_lower_vco = true; + use = true; + } + } else if (!is_lower_vco && + current_vco > best_vco) + use = true; + if (use) { + chip->divs.ns1_q0 = x; + chip->divs.ns2_q0 = y; + best_vco = current_vco; + } + } + y++; + } + } + + dev_dbg(&chip->i2c_client->dev, + "%s. best: (ns1=%u [/%u] * ns2=%u * 2 * %u) == %u", + __func__, chip->divs.ns1_q0, q0_ns1_options[chip->divs.ns1_q0], + chip->divs.ns2_q0, chip->clk[0].requested, best_vco); + return 0; +} + +/** + * idt24x_calc_divs - Calculate dividers to generate the specified frequency. + * @chip: Device data structure. contains all requested frequencies + * for all outputs. + * + * Calculate the clock dividers (dsmint, dsmfrac for vco; ns1/ns2 for q0, + * n/nfrac for q1-3) for a given target frequency. + * + * Return: 0 on success, negative errno otherwise. + */ +static int idt24x_calc_divs(struct clk_idt24x_chip *chip) +{ + u32 vco = 0; + int result; + + result = idt24x_calc_div_q0(chip); + if (result < 0) + return result; + + dev_dbg(&chip->i2c_client->dev, + "%s: after idt24x_calc_div_q0. ns1: %u [/%u], ns2: %u", + __func__, chip->divs.ns1_q0, q0_ns1_options[chip->divs.ns1_q0], + chip->divs.ns2_q0); + + chip->divs.dsmint = 0; + chip->divs.dsmfrac = 0; + + if (chip->clk[0].requested > 0) { + /* Q0 is in use and is governing the actual VCO freq */ + vco = q0_ns1_options[chip->divs.ns1_q0] * chip->divs.ns2_q0 * + 2 * chip->clk[0].requested; + } else { + u32 freq = 0; + u32 walk; + u32 min_div, max_div; + bool is_lower_vco = false; + + /* + * Q0 is not in use. Use the first requested (fractional) + * output frequency as the one controlling the VCO. + */ + for (walk = 1; walk < NUM_OUTPUTS; walk++) { + if (chip->clk[walk].requested != 0) { + freq = chip->clk[walk].requested; + break; + } + } + + if (freq == 0) { + dev_err(&chip->i2c_client->dev, + "%s: NO FREQUENCIES SPECIFIED", __func__); + return -EINVAL; + } + + /* + * First, determine the min/max div for the output frequency. + */ + min_div = IDT24x_MIN_INT_DIVIDER; + max_div = div64_u64((u64)IDT24x_VCO_MAX, freq * 2) * 2; + + dev_dbg(&chip->i2c_client->dev, + "%s: calc_divs for fractional output. freq: %u, min_div: %u, max_div: %u", + __func__, freq, min_div, max_div); + + walk = min_div; + + while (walk <= max_div) { + u32 current_vco = freq * walk; + + dev_dbg(&chip->i2c_client->dev, + "%s: calc_divs for fractional output. walk: %u, freq: %u, vco: %u", + __func__, walk, freq, vco); + if (current_vco >= IDT24x_VCO_MIN && + vco <= IDT24x_VCO_MAX) { + if (current_vco <= IDT24x_VCO_OPT) { + if (current_vco > vco || + !is_lower_vco) { + is_lower_vco = true; + vco = current_vco; + } + } else if (!is_lower_vco && current_vco > vco) { + vco = current_vco; + } + } + /* Divider must be even. */ + walk += 2; + } + } + + if (vco != 0) { + u32 pfd; + u64 rem; + int x; + + /* Setup dividers for outputs with fractional dividers. */ + for (x = 1; x < NUM_OUTPUTS; x++) { + if (chip->clk[x].requested != 0) { + /* + * The value written to the chip is half + * the calculated divider. + */ + chip->divs.nint[x - 1] = div64_u64_rem( + (u64)vco, + chip->clk[x].requested * 2, + &rem); + chip->divs.nfrac[x - 1] = div64_u64( + rem * 1 << 28, + chip->clk[x].requested * 2); + dev_dbg(&chip->i2c_client->dev, + "%s: div to get Q%i freq %u from vco %u: int part: %u, rem: %llu, frac part: %u", + __func__, x, + chip->clk[x].requested, + vco, chip->divs.nint[x - 1], rem, + chip->divs.nfrac[x - 1]); + } + } + + /* Calculate freq for pfd */ + pfd = chip->input_clk_freq * (chip->doubler_disabled ? 1 : 2); + + /* + * Calculate dsmint & dsmfrac: + * ----------------------------- + * dsm = float(vco)/float(pfd) + * dsmfrac = dsm-floor(dsm) * 2^21 + * rem = vco % pfd + * therefore: + * dsmfrac = (rem * 2^21)/pfd + */ + chip->divs.dsmint = div64_u64_rem(vco, pfd, &rem); + chip->divs.dsmfrac = div64_u64(rem * 1 << 21, pfd); + + dev_dbg(&chip->i2c_client->dev, + "%s: vco: %u, pfd: %u, dsmint: %u, dsmfrac: %u, rem: %llu", + __func__, vco, pfd, chip->divs.dsmint, + chip->divs.dsmfrac, rem); + } else { + dev_err(&chip->i2c_client->dev, + "%s: no integer divider in range found. NOT SUPPORTED.", + __func__); + return -EINVAL; + } + return 0; +} + +/** + * idt24x_enable_output - Enable/disable a particular output + * @chip: Device data structure + * @output: Output to enable/disable + * @enable: Enable (true/false) + * + * Return: passes on regmap_write return value. + */ +static int idt24x_enable_output( + struct clk_idt24x_chip *chip, u8 output, bool enable) +{ + struct clk_register_offsets offsets; + int err; + struct i2c_client *client = chip->i2c_client; + + /* + * When an output is enabled, enable it in the original + * data read from the chip and cached. Otherwise it may be + * accidentally turned off when another output is enabled. + * + * E.g., the driver starts with all outputs off in reg_out_en_x. + * Q1 is enabled with the appropriate mask. Q2 is then enabled, + * which results in Q1 being turned back off (because Q1 was off + * in reg_out_en_x). + */ + + err = idt24x_get_offsets(output, &offsets); + if (err) { + dev_err(&client->dev, + "%s: error calling idt24x_get_offsets for %d: %i", + __func__, output, err); + return err; + } + + dev_dbg(&client->dev, + "%s: q%u enable? %d. reg_out_en_x before: 0x%x, reg_out_mode_0_1 before: 0x%x, reg_out_mode_2_3 before: 0x%x, reg_qx_dis before: 0x%x", + __func__, output, enable, chip->reg_out_en_x, + chip->reg_out_mode_0_1, chip->reg_out_mode_2_3, + chip->reg_qx_dis); + + chip->reg_out_en_x = chip->reg_out_en_x & ~offsets.oe_mask; + if (enable) + chip->reg_out_en_x |= (1 << bits_to_shift(offsets.oe_mask)); + + chip->reg_qx_dis = chip->reg_qx_dis & ~offsets.dis_mask; + dev_dbg(&client->dev, + "%s: q%u enable? %d. reg_qx_dis mask: 0x%x, before checking enable: 0x%x", + __func__, output, enable, offsets.dis_mask, + chip->reg_qx_dis); + if (!enable) + chip->reg_qx_dis |= (1 << bits_to_shift(offsets.dis_mask)); + + dev_dbg(&client->dev, + "%s: q%u enable? %d. reg_out_en_x after: 0x%x, reg_qx_dis after: 0x%x", + __func__, output, enable, chip->reg_out_en_x, + chip->reg_qx_dis); + + err = i2cwrite( + client, chip->regmap, IDT24x_REG_OUTEN, chip->reg_out_en_x); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_OUTEN: %i", + __func__, err); + return err; + } + + err = i2cwrite( + client, chip->regmap, IDT24x_REG_OUTMODE0_1, + chip->reg_out_mode_0_1); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_OUTMODE0_1: %i", + __func__, err); + return err; + } + + err = i2cwrite( + client, chip->regmap, IDT24x_REG_OUTMODE2_3, + chip->reg_out_mode_2_3); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_OUTMODE2_3: %i", + __func__, err); + return err; + } + + err = i2cwrite( + client, chip->regmap, IDT24x_REG_Q_DIS, chip->reg_qx_dis); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_Q_DIS: %i", + __func__, err); + return err; + } + + return 0; +} + +/** + * idt24x_update_device - write registers to the chip + * @chip: Device data structure + * + * Write all values to hardware that we have calculated. + * + * Return: passes on regmap_bulk_write return value. + */ +static int idt24x_update_device(struct clk_idt24x_chip *chip) +{ + int err; + struct i2c_client *client = chip->i2c_client; + int x = -1; + + dev_dbg(&client->dev, + "%s: setting DSM_INT_8 (val %u @ %u)", + __func__, chip->divs.dsmint >> 8, + IDT24x_REG_DSM_INT_8); + err = i2cwritewithmask( + client, chip->regmap, IDT24x_REG_DSM_INT_8, + (chip->divs.dsmint >> 8) & IDT24x_REG_DSM_INT_8_MASK, + chip->reg_dsm_int_8, IDT24x_REG_DSM_INT_8_MASK); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_DSM_INT_8: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting DSM_INT_7_0 (val %u @ 0x%x)", + __func__, chip->divs.dsmint & 0xFF, + IDT24x_REG_DSM_INT_7_0); + err = i2cwrite( + client, chip->regmap, IDT24x_REG_DSM_INT_7_0, + chip->divs.dsmint & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_DSM_INT_7_0: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting IDT24x_REG_DSMFRAC_20_16 (val %u @ 0x%x)", + __func__, chip->divs.dsmfrac >> 16, + IDT24x_REG_DSMFRAC_20_16); + err = i2cwritewithmask( + client, chip->regmap, IDT24x_REG_DSMFRAC_20_16, + (chip->divs.dsmfrac >> 16) & IDT24x_REG_DSMFRAC_20_16_MASK, + chip->reg_dsm_int_8, IDT24x_REG_DSMFRAC_20_16_MASK); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_DSMFRAC_20_16: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting IDT24x_REG_DSMFRAC_15_8 (val %u @ 0x%x)", + __func__, (chip->divs.dsmfrac >> 8) & 0xFF, + IDT24x_REG_DSMFRAC_15_8); + err = i2cwrite( + client, chip->regmap, IDT24x_REG_DSMFRAC_15_8, + (chip->divs.dsmfrac >> 8) & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_DSMFRAC_15_8: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting IDT24x_REG_DSMFRAC_7_0 (val %u @ 0x%x)", + __func__, chip->divs.dsmfrac & 0xFF, + IDT24x_REG_DSMFRAC_7_0); + err = i2cwrite( + client, chip->regmap, IDT24x_REG_DSMFRAC_7_0, + chip->divs.dsmfrac & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_DSMFRAC_7_0: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting IDT24x_REG_NS1_Q0 (val %u @ 0x%x)", + __func__, chip->divs.ns1_q0, IDT24x_REG_NS1_Q0); + err = i2cwritewithmask( + client, chip->regmap, IDT24x_REG_NS1_Q0, + chip->divs.ns1_q0 & IDT24x_REG_NS1_Q0_MASK, + chip->reg_ns1_q0, IDT24x_REG_NS1_Q0_MASK); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_NS1_Q0: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting IDT24x_REG_NS2_Q0_15_8 (val %u @ 0x%x)", + __func__, (chip->divs.ns2_q0 >> 8) & 0xFF, + IDT24x_REG_NS2_Q0_15_8); + err = i2cwrite( + client, chip->regmap, IDT24x_REG_NS2_Q0_15_8, + (chip->divs.ns2_q0 >> 8) & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_NS2_Q0_15_8: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting IDT24x_REG_NS2_Q0_7_0 (val %u @ 0x%x)", + __func__, chip->divs.ns2_q0 & 0xFF, + IDT24x_REG_NS2_Q0_7_0); + err = i2cwrite( + client, chip->regmap, IDT24x_REG_NS2_Q0_7_0, + chip->divs.ns2_q0 & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting IDT24x_REG_NS2_Q0_7_0: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: calling idt24x_enable_output for Q0. requestedFreq: %u", + __func__, chip->clk[0].requested); + idt24x_enable_output(chip, 0, chip->clk[0].requested != 0); + + dev_dbg(&client->dev, + "%s: writing values for q1-q3", __func__); + for (x = 1; x < NUM_OUTPUTS; x++) { + struct clk_register_offsets offsets; + + if (chip->clk[x].requested != 0) { + dev_dbg(&client->dev, + "%s: calling idt24x_get_offsets for %u", + __func__, x); + err = idt24x_get_offsets(x, &offsets); + if (err) { + dev_err(&client->dev, + "%s: error calling idt24x_get_offsets: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: (q%u, nint: %u, nfrac: %u)", + __func__, x, chip->divs.nint[x - 1], + chip->divs.nfrac[x - 1]); + + dev_dbg(&client->dev, + "%s: setting n_17_16_offset (q%u, val %u @ 0x%x)", + __func__, x, + chip->divs.nint[x - 1] >> 16, + offsets.n_17_16_offset); + err = i2cwritewithmask( + client, chip->regmap, offsets.n_17_16_offset, + (chip->divs.nint[x - 1] >> 16) & + offsets.n_17_16_mask, + chip->reg_n_qx_17_16[x - 1], + offsets.n_17_16_mask); + if (err) { + dev_err(&client->dev, + "%s: error setting n_17_16_offset: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting n_15_8_offset (q%u, val %u @ 0x%x)", + __func__, x, + (chip->divs.nint[x - 1] >> 8) & 0xFF, + offsets.n_15_8_offset); + err = i2cwrite( + client, chip->regmap, offsets.n_15_8_offset, + (chip->divs.nint[x - 1] >> 8) & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting n_15_8_offset: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting n_7_0_offset (q%u, val %u @ 0x%x)", + __func__, x, + chip->divs.nint[x - 1] & 0xFF, + offsets.n_7_0_offset); + err = i2cwrite( + client, chip->regmap, offsets.n_7_0_offset, + chip->divs.nint[x - 1] & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting n_7_0_offset: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting nfrac_27_24_offset (q%u, val %u @ 0x%x)", + __func__, x, + (chip->divs.nfrac[x - 1] >> 24), + offsets.nfrac_27_24_offset); + err = i2cwritewithmask( + client, chip->regmap, + offsets.nfrac_27_24_offset, + (chip->divs.nfrac[x - 1] >> 24) & + offsets.nfrac_27_24_mask, + chip->reg_nfrac_qx_27_24[x - 1], + offsets.nfrac_27_24_mask); + if (err) { + dev_err(&client->dev, + "%s: error setting nfrac_27_24_offset: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting nfrac_23_16_offset (q%u, val %u @ 0x%x)", + __func__, x, + (chip->divs.nfrac[x - 1] >> 16) & 0xFF, + offsets.nfrac_23_16_offset); + err = i2cwrite( + client, chip->regmap, + offsets.nfrac_23_16_offset, + (chip->divs.nfrac[x - 1] >> 16) & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting nfrac_23_16_offset: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting nfrac_15_8_offset (q%u, val %u @ 0x%x)", + __func__, x, + (chip->divs.nfrac[x - 1] >> 8) & 0xFF, + offsets.nfrac_15_8_offset); + err = i2cwrite( + client, chip->regmap, + offsets.nfrac_15_8_offset, + (chip->divs.nfrac[x - 1] >> 8) & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting nfrac_15_8_offset: %i", + __func__, err); + return err; + } + + dev_dbg(&client->dev, + "%s: setting nfrac_7_0_offset (q%u, val %u @ 0x%x)", + __func__, x, + chip->divs.nfrac[x - 1] & 0xFF, + offsets.nfrac_7_0_offset); + err = i2cwrite( + client, chip->regmap, offsets.nfrac_7_0_offset, + chip->divs.nfrac[x - 1] & 0xFF); + if (err) { + dev_err(&client->dev, + "%s: error setting nfrac_7_0_offset: %i", + __func__, err); + return err; + } + } + idt24x_enable_output(chip, x, + chip->clk[x].requested != 0); + chip->clk[x].actual = chip->clk[x].requested; + } + return 0; +} + +/** + * idt24x_set_frequency - Adjust output frequency on the attached chip. + * @chip: Device data structure, including all requested frequencies. + * + * Return: 0 on success. + */ +int idt24x_set_frequency(struct clk_idt24x_chip *chip) +{ + int err; + struct i2c_client *client = chip->i2c_client; + int x; + bool all_disabled = true; + + for (x = 0; x < NUM_OUTPUTS; x++) { + if (chip->clk[x].requested == 0) { + idt24x_enable_output(chip, x, false); + chip->clk[x].actual = 0; + } else { + all_disabled = false; + } + } + + if (all_disabled) + /* + * no requested frequencies, so nothing else to calculate + * or write to the chip. If the consumer wants to disable + * all outputs, they can request 0 for all frequencies. + */ + return 0; + + if (chip->input_clk_freq == 0) { + dev_err(&client->dev, + "%s: no input frequency; can't continue.", __func__); + return -EINVAL; + } + + err = idt24x_calc_divs(chip); + if (err) { + dev_err(&client->dev, + "%s: error calling idt24x_calc_divs: %i", + __func__, err); + return err; + } + + err = idt24x_update_device(chip); + if (err) { + dev_err(&client->dev, + "%s: error updating the device: %i", + __func__, err); + return err; + } + + return 0; +} diff --git a/drivers/clk/idt/clk-idt8t49n24x-core.h b/drivers/clk/idt/clk-idt8t49n24x-core.h new file mode 100644 index 000000000000..247ec070c621 --- /dev/null +++ b/drivers/clk/idt/clk-idt8t49n24x-core.h @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* clk-idt8t49n24x-core.h - Program 8T49N24x settings via I2C (common code) + * + * Copyright (C) 2018, Integrated Device Technology, Inc. <david.cater@idt.com> + * + * See https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html + * This program is distributed "AS IS" and WITHOUT ANY WARRANTY; + * including the implied warranties of MERCHANTABILITY, FITNESS FOR + * A PARTICULAR PURPOSE, or NON-INFRINGEMENT. + */ + +#ifndef __IDT_CLK_IDT8T49N24X_CORE_H_ +#define __IDT_CLK_IDT8T49N24X_CORE_H_ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/regmap.h> + +/* + * The configurations in the settings file have 0x317 registers (last offset + * is 0x316). + */ +#define NUM_CONFIG_REGISTERS 0x317 +#define NUM_INPUTS 2 +#define NUM_OUTPUTS 4 +#define DEBUGFS_BUFFER_LENGTH 200 +#define WRITE_BLOCK_SIZE 32 + +/* Non output-specific registers */ +#define IDT24x_REG_DBL_DIS 0x6C +#define IDT24x_REG_DBL_DIS_MASK 0x01 +#define IDT24x_REG_DSM_INT_8 0x25 +#define IDT24x_REG_DSM_INT_8_MASK 0x01 +#define IDT24x_REG_DSM_INT_7_0 0x26 +#define IDT24x_REG_DSMFRAC_20_16 0x28 +#define IDT24x_REG_DSMFRAC_20_16_MASK 0x1F +#define IDT24x_REG_DSMFRAC_15_8 0x29 +#define IDT24x_REG_DSMFRAC_7_0 0x2A +#define IDT24x_REG_OUTEN 0x39 +#define IDT24x_REG_OUTMODE0_1 0x3E +#define IDT24x_REG_OUTMODE2_3 0x3D +#define IDT24x_REG_Q_DIS 0x6F + +/* Q0 */ +#define IDT24x_REG_OUTEN0_MASK 0x01 +#define IDT24x_REG_OUTMODE0_MASK 0x0E +#define IDT24x_REG_Q0_DIS_MASK 0x01 +#define IDT24x_REG_NS1_Q0 0x3F +#define IDT24x_REG_NS1_Q0_MASK 0x03 +#define IDT24x_REG_NS2_Q0_15_8 0x40 +#define IDT24x_REG_NS2_Q0_7_0 0x41 + +/* Q1 */ +#define IDT24x_REG_OUTEN1_MASK 0x02 +#define IDT24x_REG_OUTMODE1_MASK 0xE0 +#define IDT24x_REG_Q1_DIS_MASK 0x02 +#define IDT24x_REG_N_Q1_17_16 0x42 +#define IDT24x_REG_N_Q1_17_16_MASK 0x03 +#define IDT24x_REG_N_Q1_15_8 0x43 +#define IDT24x_REG_N_Q1_7_0 0x44 +#define IDT24x_REG_NFRAC_Q1_27_24 0x57 +#define IDT24x_REG_NFRAC_Q1_27_24_MASK 0x0F +#define IDT24x_REG_NFRAC_Q1_23_16 0x58 +#define IDT24x_REG_NFRAC_Q1_15_8 0x59 +#define IDT24x_REG_NFRAC_Q1_7_0 0x5A + +/* Q2 */ +#define IDT24x_REG_OUTEN2_MASK 0x04 +#define IDT24x_REG_OUTMODE2_MASK 0x0E +#define IDT24x_REG_Q2_DIS_MASK 0x04 +#define IDT24x_REG_N_Q2_17_16 0x45 +#define IDT24x_REG_N_Q2_17_16_MASK 0x03 +#define IDT24x_REG_N_Q2_15_8 0x46 +#define IDT24x_REG_N_Q2_7_0 0x47 +#define IDT24x_REG_NFRAC_Q2_27_24 0x5B +#define IDT24x_REG_NFRAC_Q2_27_24_MASK 0x0F +#define IDT24x_REG_NFRAC_Q2_23_16 0x5C +#define IDT24x_REG_NFRAC_Q2_15_8 0x5D +#define IDT24x_REG_NFRAC_Q2_7_0 0x5E + +/* Q3 */ +#define IDT24x_REG_OUTEN3_MASK 0x08 +#define IDT24x_REG_OUTMODE3_MASK 0xE0 +#define IDT24x_REG_Q3_DIS_MASK 0x08 +#define IDT24x_REG_N_Q3_17_16 0x48 +#define IDT24x_REG_N_Q3_17_16_MASK 0x03 +#define IDT24x_REG_N_Q3_15_8 0x49 +#define IDT24x_REG_N_Q3_7_0 0x4A +#define IDT24x_REG_NFRAC_Q3_27_24 0x5F +#define IDT24x_REG_NFRAC_Q3_27_24_MASK 0x0F +#define IDT24x_REG_NFRAC_Q3_23_16 0x60 +#define IDT24x_REG_NFRAC_Q3_15_8 0x61 +#define IDT24x_REG_NFRAC_Q3_7_0 0x62 + +/** + * struct idt24x_output - device output information + * @hw: hw registration info for this specific output clcok. This gets + * passed as an argument to CCF api calls (e.g., set_rate). + * container_of can then be used to get the reference to this + * struct. + * @chip: store a reference to the parent device structure. container_of + * cannot be used to get to the parent device structure from + * idt24x_output, because clk_idt24x_chip contains an array of + * output structs (for future enhancements to support devices + * with different numbers of output clocks). + * @index: identifies output on the chip; used in debug statements + * @requested: requested output clock frequency (in Hz) + * @actual: actual output clock frequency (in Hz). Will only be set after + * successful update of the device. + * @debug_freq: stores value for debugfs file. Use this instead of requested + * struct var because debugfs expects u64, not u32. + */ +struct idt24x_output { + struct clk_hw hw; + struct clk_idt24x_chip *chip; + u8 index; + u32 requested; + u32 actual; + u64 debug_freq; +}; + +/** + * struct idt24x_dividers - output dividers + * @dsmint: int component of feedback divider for VCO (2-stage divider) + * @dsmfrac: fractional component of feedback divider for VCO + * @ns1_q0: ns1 divider component for Q0 + * @ns2_q0: ns2 divider component for Q0 + * @nint: int divider component for Q1-3 + * @nfrac: fractional divider component for Q1-3 + */ +struct idt24x_dividers { + u16 dsmint; + u32 dsmfrac; + + u8 ns1_q0; + u16 ns2_q0; + + u32 nint[3]; + u32 nfrac[3]; +}; + +/** + * struct clk_idt24x_chip - device info for chip + * @regmap: register map used to perform i2c writes to the chip + * @i2c_client: i2c_client struct passed to probe + * @min_freq: min frequency for this chip + * @max_freq: max frequency for this chip + * @settings: filled in if full register map is specified in the DT + * @has_settings: true if settings array is valid + * @input_clk: ptr to input clock specified in DT + * @input_clk_num: which input clock was specified. 0-based. A value of + * NUM_INPUTS indicates that a XTAL is used as the input. + * @input_clk_nb: notification support (if input clk changes) + * @input_clk_freq: current freq of input_clk + * @doubler_disabled: whether input doubler is enabled. This value is read + * from the hw on probe (in case it is set in @settings). + * @clk: array of outputs. One entry per output supported by the + * chip. Frequencies requested via the ccf api will be + * recorded in this array. + * @reg_dsm_int_8: record current value from hw to avoid modifying + * when writing register values + * @reg_dsm_frac_20_16: record current value + * @reg_out_en_x: record current value + * @reg_out_mode_0_1: record current value + * @reg_out_mode_2_3: record current value + * @reg_qx_dis: record current value + * @reg_ns1_q0: record current value + * @reg_n_qx_17_16: record current value + * @reg_nfrac_qx_27_24: record current value + * @divs: output divider values for all outputs + * @debugfs_dirroot: debugfs support + * @debugfs_fileaction: debugfs support + * @debugfs_filei2c: debugfs support + * @debugfs_map: debugfs support + * @dbg_cache: debugfs support + * @debugfs_fileqfreq: debugfs support + */ +struct clk_idt24x_chip { + struct regmap *regmap; + struct i2c_client *i2c_client; + + u32 min_freq; + u32 max_freq; + + u8 settings[NUM_CONFIG_REGISTERS]; + + bool has_settings; + + struct clk *input_clk; + int input_clk_num; + struct notifier_block input_clk_nb; + u32 input_clk_freq; + + bool doubler_disabled; + + struct idt24x_output clk[NUM_OUTPUTS]; + + unsigned int reg_dsm_int_8; + unsigned int reg_dsm_frac_20_16; + unsigned int reg_out_en_x; + unsigned int reg_out_mode_0_1; + unsigned int reg_out_mode_2_3; + unsigned int reg_qx_dis; + unsigned int reg_ns1_q0; + unsigned int reg_n_qx_17_16[3]; + unsigned int reg_nfrac_qx_27_24[3]; + + struct idt24x_dividers divs; + + struct dentry *debugfs_dirroot, *debugfs_fileaction, *debugfs_filei2c, + *debugfs_map; + char dbg_cache[DEBUGFS_BUFFER_LENGTH]; + struct dentry *debugfs_fileqfreq[4]; +}; + +#define to_idt24x_output(_hw) \ + container_of(_hw, struct idt24x_output, hw) +#define to_clk_idt24x_from_client(_client) \ + container_of(_client, struct clk_idt24x_chip, i2c_client) +#define to_clk_idt24x_from_nb(_nb) \ + container_of(_nb, struct clk_idt24x_chip, input_clk_nb) + +/** + * struct clk_register_offsets - register offsets for current context + * @oe_offset: offset for current output enable and mode + * @oe_mask: mask for current output enable + * @dis_mask: mask for current output disable + * @n_17_16_offset: offset for current output int divider (bits 17:16) + * @n_17_16_mask: mask for current output int divider (bits 17:16) + * @n_15_8_offset: offset for current output int divider (bits 15:8) + * @n_7_0_offset: offset for current output int divider (bits 7:0) + * @nfrac_27_24_offset: offset for current output frac divider (bits 27:24) + * @nfrac_27_24_mask: mask for current output frac divider (bits 27:24) + * @nfrac_23_16_offset: offset for current output frac divider (bits 23:16) + * @nfrac_15_8_offset: offset for current output frac divider (bits 15:8) + * @nfrac_7_0_offset: offset for current output frac divider (bits 7:0) + * @ns1_offset: offset for stage 1 div for output Q0 + * @ns1_offset_mask: mask for stage 1 div for output Q0 + * @ns2_15_8_offset: offset for stage 2 div for output Q0 (bits 15:8) + * @ns2_7_0_offset: offset for stage 2 div for output Q0 (bits 7:0) + */ +struct clk_register_offsets { + u16 oe_offset; + u8 oe_mask; + u8 dis_mask; + + u16 n_17_16_offset; + u8 n_17_16_mask; + u16 n_15_8_offset; + u16 n_7_0_offset; + u16 nfrac_27_24_offset; + u8 nfrac_27_24_mask; + u16 nfrac_23_16_offset; + u16 nfrac_15_8_offset; + u16 nfrac_7_0_offset; + + u16 ns1_offset; + u8 ns1_offset_mask; + u16 ns2_15_8_offset; + u16 ns2_7_0_offset; +}; + +int bits_to_shift(unsigned int mask); +int i2cwritebulk( + struct i2c_client *client, struct regmap *map, + unsigned int reg, u8 val[], size_t val_count); +int idt24x_get_offsets( + u8 output_num, + struct clk_register_offsets *offsets); +int idt24x_set_frequency(struct clk_idt24x_chip *chip); + +#endif /* __IDT_CLK_IDT8T49N24X_CORE_H_ */ diff --git a/drivers/clk/idt/clk-idt8t49n24x-debugfs.c b/drivers/clk/idt/clk-idt8t49n24x-debugfs.c new file mode 100644 index 000000000000..967a9df8701c --- /dev/null +++ b/drivers/clk/idt/clk-idt8t49n24x-debugfs.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 +/* clk-idt8t49n24x-debugfs.c - Debugfs support for 8T49N24x + * + * Copyright (C) 2018, Integrated Device Technology, Inc. <david.cater@idt.com> + * + * See https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html + * This program is distributed "AS IS" and WITHOUT ANY WARRANTY; + * including the implied warranties of MERCHANTABILITY, FITNESS FOR + * A PARTICULAR PURPOSE, or NON-INFRINGEMENT. + */ + +#include <linux/debugfs.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "clk-idt8t49n24x-debugfs.h" + +static struct clk_idt24x_chip *idt24x_chip_fordebugfs; + +static int idt24x_read_all_settings( + struct clk_idt24x_chip *chip, char *output_buffer, int count) +{ + u8 settings[NUM_CONFIG_REGISTERS]; + int err = 0; + int x; + + err = regmap_bulk_read( + chip->regmap, 0x0, settings, NUM_CONFIG_REGISTERS); + if (!err) { + output_buffer[0] = '\0'; + for (x = 0; x < ARRAY_SIZE(settings); x++) { + char dbg[4]; + + if ((strlen(output_buffer) + 4) > count) + return -EINVAL; + sprintf(dbg, "%02x ", settings[x]); + strcat(output_buffer, dbg); + } + } + return err; +} + +/** + * idt24x_debugfs_writer_action - Write handler for the "action" debugfs file. + * @fp: file pointer + * @user_buffer: buffer of text written to file + * @count: size of text in buffer + * @position: pass in current position, return new position + * + * Return: result of call to simple_write_to_buffer + * + * Use the "action" file as a trigger for setting all requested + * rates. The driver doesn't get any notification when the files + * representing the Qx outputs are written to, so something else is + * needed to notify the driver that the device should be udpated. + * + * It doesn't matter what you write to the action debugs file. When the + * handler is called, the device will be updated. + */ +static ssize_t idt24x_debugfs_writer_action( + struct file *fp, const char __user *user_buffer, + size_t count, loff_t *position) +{ + int err = 0; + int x; + u32 freq; + bool needs_update = true; + struct i2c_client *client = idt24x_chip_fordebugfs->i2c_client; + + if (count > DEBUGFS_BUFFER_LENGTH) + return -EINVAL; + + for (x = 0; x < NUM_OUTPUTS; x++) { + freq = idt24x_chip_fordebugfs->clk[x].debug_freq; + if (freq) { + needs_update = false; + dev_dbg(&client->dev, + "%s: calling clk_set_rate with debug frequency for Q%i", + __func__, x); + err = clk_set_rate( + idt24x_chip_fordebugfs->clk[x].hw.clk, freq); + if (err) { + dev_err(&client->dev, + "error calling clk_set_rate for Q%i (%i)\n", + x, err); + } + } else { + needs_update = true; + idt24x_chip_fordebugfs->clk[x].requested = 0; + dev_dbg(&client->dev, + "%s: debug frequency for Q%i not set; make sure clock is disabled", + __func__, x); + } + } + + if (needs_update) { + dev_dbg(&client->dev, + "%s: calling idt24x_set_frequency to ensure any clocks that should be disabled are turned off.", + __func__); + err = idt24x_set_frequency(idt24x_chip_fordebugfs); + if (err) { + dev_err(&idt24x_chip_fordebugfs->i2c_client->dev, + "%s: error calling idt24x_set_frequency (%i)\n", + __func__, err); + return err; + } + } + + return simple_write_to_buffer( + idt24x_chip_fordebugfs->dbg_cache, DEBUGFS_BUFFER_LENGTH, + position, user_buffer, count); +} + +/** + * idt24x_debugfs_reader_action - Read the "action" debugfs file. + * @fp: file pointer + * @user_buffer: buffer of text written to file + * @count: size of text in buffer + * @position: pass in current position, return new position + * + * Return: whatever was last written to the "action" debugfs file. + */ +static ssize_t idt24x_debugfs_reader_action( + struct file *fp, char __user *user_buffer, size_t count, + loff_t *position) +{ + return simple_read_from_buffer( + user_buffer, count, position, idt24x_chip_fordebugfs->dbg_cache, + DEBUGFS_BUFFER_LENGTH); +} + +/** + * idt24x_debugfs_reader_map - display the current registers on the device + * @fp: file pointer + * @user_buffer: buffer of text written to file + * @count: size of text in buffer + * @position: pass in current position, return new position + * + * Reads the current register map from the attached chip via I2C and + * returns it. + * + * Return: result of call to simple_read_from_buffer + */ +static ssize_t idt24x_debugfs_reader_map( + struct file *fp, char __user *user_buffer, size_t count, + loff_t *position) +{ + int err = 0; + char *buf = kzalloc(5000, GFP_KERNEL); + + dev_dbg(&idt24x_chip_fordebugfs->i2c_client->dev, + "calling idt24x_read_all_settings (count: %zu)\n", count); + err = idt24x_read_all_settings(idt24x_chip_fordebugfs, buf, 5000); + if (err) { + dev_err(&idt24x_chip_fordebugfs->i2c_client->dev, + "error calling idt24x_read_all_settings (%i)\n", err); + return 0; + } + /* TMGCDR-1456. We're returning 1 byte too few. */ + err = simple_read_from_buffer( + user_buffer, count, position, buf, strlen(buf)); + kfree(buf); + return err; +} + +/** + * idt24x_handle_i2c_debug_token - process "token" written to the i2c file + * @dev: pointer to device structure + * @token: pointer to current char being examined + * @reg: pass in current register, or return register from token. + * @val: resulting array of bytes being parsed + * @nextbyte: position in val array to store next byte + * + * Utility function to operate on the current "token" (from within a + * space-delimited string) written to the i2c debugfs file. It will + * either be a register offset or a byte to be added to the val array. + * If it is added to the val array, auto-increment nextbyte. + * + * Return: 0 for success + */ +static int idt24x_handle_i2c_debug_token( + const struct device *dev, char *token, unsigned int *reg, + u8 val[], u16 *nextbyte) +{ + int err = 0; + + dev_dbg(dev, "got token (%s)\n", token); + if (*reg == -1) { + err = kstrtouint(token, 16, reg); + if (!err) + dev_dbg(dev, "hex register address == 0x%x\n", *reg); + } else { + u8 temp; + + err = kstrtou8(token, 16, &temp); + if (!err) { + dev_dbg(dev, "data byte == 0x%x\n", temp); + val[*nextbyte] = temp; + *nextbyte += 1; + } + } + if (err == -ERANGE) + dev_err(dev, "ERANGE error when parsing data\n"); + else if (err == -EINVAL) + dev_err(dev, "EINVAL error when parsing data\n"); + else if (err) + dev_err(dev, "error when parsing data: %i\n", err); + return err; +} + +/** + * idt24x_debugfs_writer_i2c - debugfs handler for i2c file + * @fp: file pointer + * @user_buffer: buffer of text written to file + * @count: size of text in buffer + * @position: pass in current position, return new position + * + * Handler for the "i2c" debugfs file. Write to this file to write bytes + * via I2C to a particular offset. + * + * Usage: echo 006c 01 02 0D FF > i2c + * + * First 4 chars are the 2-byte i2c register offset. Then follow that + * with a sequence of 2-char bytes in hex format that you want to write + * starting at that offset. + * + * Return: result of simple_write_to_buffer + */ +static ssize_t idt24x_debugfs_writer_i2c(struct file *fp, + const char __user *user_buffer, + size_t count, loff_t *position) +{ + int err = 0; + int x = 0; + int start = 0; + ssize_t written; + unsigned int reg = -1; + u8 val[WRITE_BLOCK_SIZE]; + u16 nextbyte = 0; + char token[16]; + + if (count > DEBUGFS_BUFFER_LENGTH) + return -EINVAL; + + written = simple_write_to_buffer( + idt24x_chip_fordebugfs->dbg_cache, DEBUGFS_BUFFER_LENGTH, + position, user_buffer, count); + if (written != count) { + dev_dbg(&idt24x_chip_fordebugfs->i2c_client->dev, + "write count != expected count"); + return written; + } + + for (x = 0; x < count; x++) { + token[x - start] = idt24x_chip_fordebugfs->dbg_cache[x]; + if (idt24x_chip_fordebugfs->dbg_cache[x] == ' ') { + token[x - start] = '\0'; + err = idt24x_handle_i2c_debug_token( + &idt24x_chip_fordebugfs->i2c_client->dev, + token, ®, val, &nextbyte); + if (err) + break; + start = x + 1; + } + } + + /* handle the last token */ + if (!err) { + token[count - start] = '\0'; + err = idt24x_handle_i2c_debug_token( + &idt24x_chip_fordebugfs->i2c_client->dev, token, ®, + val, &nextbyte); + } + + if (!err && reg != -1 && nextbyte > 0) { + err = i2cwritebulk( + idt24x_chip_fordebugfs->i2c_client, + idt24x_chip_fordebugfs->regmap, + reg, val, nextbyte); + if (err) { + dev_err(&idt24x_chip_fordebugfs->i2c_client->dev, + "error writing data chip (%i)\n", err); + return err; + } + dev_dbg(&idt24x_chip_fordebugfs->i2c_client->dev, + "successfully wrote i2c data to chip"); + } + + return written; +} + +static const struct file_operations idt24x_fops_debug_action = { + .read = idt24x_debugfs_reader_action, + .write = idt24x_debugfs_writer_action, +}; + +static const struct file_operations idt24x_fops_debug_map = { + .read = idt24x_debugfs_reader_map +}; + +static const struct file_operations idt24x_fops_debug_i2c = { + .write = idt24x_debugfs_writer_i2c, +}; + +/** + * idt24x_expose_via_debugfs - Set up all debugfs files + * @client: pointer to i2c_client structure + * @chip: Device data structure + * + * Sets up all debugfs files to use for debugging the driver. + * Return: error code. 0 if success or debugfs doesn't appear to be enabled. + */ +int idt24x_expose_via_debugfs(struct i2c_client *client, + struct clk_idt24x_chip *chip) +{ + int output_num; + + /* + * create root directory in /sys/kernel/debugfs + */ + chip->debugfs_dirroot = debugfs_create_dir("idt24x", NULL); + if (!chip->debugfs_dirroot) { + /* debugfs probably not enabled. Don't fail the probe. */ + return 0; + } + + /* + * create files in the root directory. This requires read and + * write file operations + */ + chip->debugfs_fileaction = debugfs_create_file( + "action", 0644, chip->debugfs_dirroot, NULL, + &idt24x_fops_debug_action); + if (!chip->debugfs_fileaction) { + dev_err(&client->dev, + "%s: error creating action file", __func__); + return (-ENODEV); + } + + chip->debugfs_map = debugfs_create_file( + "map", 0444, chip->debugfs_dirroot, NULL, + &idt24x_fops_debug_map); + if (!chip->debugfs_map) { + dev_err(&client->dev, + "%s: error creating map file", __func__); + return (-ENODEV); + } + + for (output_num = 0; output_num < NUM_OUTPUTS; output_num++) { + char name[5]; + + sprintf(name, "q%d", output_num); + chip->debugfs_fileqfreq[output_num] = debugfs_create_u64( + name, 0644, chip->debugfs_dirroot, + &chip->clk[output_num].debug_freq); + if (!chip->debugfs_fileqfreq[output_num]) { + dev_err(&client->dev, + "%s: error creating %s debugfs file", + __func__, name); + return (-ENODEV); + } + } + + chip->debugfs_filei2c = debugfs_create_file( + "i2c", 0644, chip->debugfs_dirroot, NULL, + &idt24x_fops_debug_i2c); + if (!chip->debugfs_filei2c) { + dev_err(&client->dev, + "%s: error creating i2c file", __func__); + return (-ENODEV); + } + + dev_dbg(&client->dev, "%s: success", __func__); + idt24x_chip_fordebugfs = chip; + return 0; +} + +void idt24x_cleanup_debugfs(struct clk_idt24x_chip *chip) +{ + debugfs_remove_recursive(chip->debugfs_dirroot); +} diff --git a/drivers/clk/idt/clk-idt8t49n24x-debugfs.h b/drivers/clk/idt/clk-idt8t49n24x-debugfs.h new file mode 100644 index 000000000000..673016c8e747 --- /dev/null +++ b/drivers/clk/idt/clk-idt8t49n24x-debugfs.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* clk-idt8t49n24x-debugfs.h - Debugfs support for 8T49N24x + * + * Copyright (C) 2018, Integrated Device Technology, Inc. <david.cater@idt.com> + * + * See https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html + * This program is distributed "AS IS" and WITHOUT ANY WARRANTY; + * including the implied warranties of MERCHANTABILITY, FITNESS FOR + * A PARTICULAR PURPOSE, or NON-INFRINGEMENT. + */ + +#ifndef __IDT_CLK_IDT8T49N24X_DEBUGFS_H_ +#define __IDT_CLK_IDT8T49N24X_DEBUGFS_H_ + +#include "clk-idt8t49n24x-core.h" + +int idt24x_expose_via_debugfs(struct i2c_client *client, + struct clk_idt24x_chip *chip); +void idt24x_cleanup_debugfs(struct clk_idt24x_chip *chip); + +#endif /* __IDT_CLK_IDT8T49N24X_DEBUGFS_H_*/ diff --git a/drivers/clk/idt/clk-idt8t49n24x.c b/drivers/clk/idt/clk-idt8t49n24x.c new file mode 100644 index 000000000000..79289a5a1934 --- /dev/null +++ b/drivers/clk/idt/clk-idt8t49n24x.c @@ -0,0 +1,641 @@ +// SPDX-License-Identifier: GPL-2.0 +/* clk-idt8t49n24x.c - Program 8T49N24x settings via I2C. + * + * Copyright (C) 2018, Integrated Device Technology, Inc. <david.cater@idt.com> + * + * See https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html + * This program is distributed "AS IS" and WITHOUT ANY WARRANTY; + * including the implied warranties of MERCHANTABILITY, FITNESS FOR + * A PARTICULAR PURPOSE, or NON-INFRINGEMENT. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "clk-idt8t49n24x-core.h" +#include "clk-idt8t49n24x-debugfs.h" + +#define OUTPUTMODE_HIGHZ 0 +#define OUTPUTMODE_LVDS 2 +#define IDT24x_MIN_FREQ 1000000L +#define IDT24x_MAX_FREQ 300000000L +#define DRV_NAME "idt8t49n24x" + +enum clk_idt24x_variant { + idt24x +}; + +static u32 mask_and_shift(u32 value, u8 mask) +{ + value &= mask; + return value >> bits_to_shift(mask); +} + +/** + * idt24x_set_output_mode - Set the mode for a particular clock + * output in the register. + * @reg: The current register value before setting the mode. + * @mask: The bitmask identifying where in the register the + * output mode is stored. + * @mode: The mode to set. + * + * Return: the new register value with the specified mode bits set. + */ +static int idt24x_set_output_mode(u32 reg, u8 mask, u8 mode) +{ + if (((reg & mask) >> bits_to_shift(mask)) == OUTPUTMODE_HIGHZ) { + reg = reg & ~mask; + reg |= (OUTPUTMODE_LVDS << bits_to_shift(mask)); + } + return reg; +} + +/** + * idt24x_read_from_hw - Get the current values on the hw + * @chip: Device data structure + * + * Return: 0 on success, negative errno otherwise. + */ +static int idt24x_read_from_hw(struct clk_idt24x_chip *chip) +{ + int err; + struct i2c_client *client = chip->i2c_client; + u32 tmp, tmp2; + u8 output; + + err = regmap_read(chip->regmap, IDT24x_REG_DSM_INT_8, + &chip->reg_dsm_int_8); + if (err) { + dev_err(&client->dev, + "%s: error reading IDT24x_REG_DSM_INT_8: %i", + __func__, err); + return err; + } + dev_dbg(&client->dev, "%s: reg_dsm_int_8: 0x%x", + __func__, chip->reg_dsm_int_8); + + err = regmap_read(chip->regmap, IDT24x_REG_DSMFRAC_20_16_MASK, + &chip->reg_dsm_frac_20_16); + if (err) { + dev_err(&client->dev, + "%s: error reading IDT24x_REG_DSMFRAC_20_16_MASK: %i", + __func__, err); + return err; + } + dev_dbg(&client->dev, "%s: reg_dsm_frac_20_16: 0x%x", + __func__, chip->reg_dsm_frac_20_16); + + err = regmap_read(chip->regmap, IDT24x_REG_OUTEN, &chip->reg_out_en_x); + if (err) { + dev_err(&client->dev, + "%s: error reading IDT24x_REG_OUTEN: %i", + __func__, err); + return err; + } + dev_dbg(&client->dev, "%s: reg_out_en_x: 0x%x", + __func__, chip->reg_out_en_x); + + err = regmap_read(chip->regmap, IDT24x_REG_OUTMODE0_1, &tmp); + if (err) { + dev_err(&client->dev, + "%s: error reading IDT24x_REG_OUTMODE0_1: %i", + __func__, err); + return err; + } + + tmp2 = idt24x_set_output_mode( + tmp, IDT24x_REG_OUTMODE0_MASK, OUTPUTMODE_LVDS); + tmp2 = idt24x_set_output_mode( + tmp2, IDT24x_REG_OUTMODE1_MASK, OUTPUTMODE_LVDS); + dev_dbg(&client->dev, + "%s: reg_out_mode_0_1 original: 0x%x. After setting OUT0/1 to LVDS if necessary: 0x%x", + __func__, tmp, tmp2); + chip->reg_out_mode_0_1 = tmp2; + + err = regmap_read(chip->regmap, IDT24x_REG_OUTMODE2_3, &tmp); + if (err) { + dev_err(&client->dev, + "%s: error reading IDT24x_REG_OUTMODE2_3: %i", + __func__, err); + return err; + } + + tmp2 = idt24x_set_output_mode( + tmp, IDT24x_REG_OUTMODE2_MASK, OUTPUTMODE_LVDS); + tmp2 = idt24x_set_output_mode( + tmp2, IDT24x_REG_OUTMODE3_MASK, OUTPUTMODE_LVDS); + dev_dbg(&client->dev, + "%s: reg_out_mode_2_3 original: 0x%x. After setting OUT2/3 to LVDS if necessary: 0x%x", + __func__, tmp, tmp2); + chip->reg_out_mode_2_3 = tmp2; + + err = regmap_read(chip->regmap, IDT24x_REG_Q_DIS, &chip->reg_qx_dis); + if (err) { + dev_err(&client->dev, + "%s: error reading IDT24x_REG_Q_DIS: %i", + __func__, err); + return err; + } + dev_dbg(&client->dev, "%s: reg_qx_dis: 0x%x", + __func__, chip->reg_qx_dis); + + err = regmap_read(chip->regmap, IDT24x_REG_NS1_Q0, &chip->reg_ns1_q0); + if (err) { + dev_err(&client->dev, + "%s: error reading IDT24x_REG_NS1_Q0: %i", + __func__, err); + return err; + } + dev_dbg(&client->dev, "%s: reg_ns1_q0: 0x%x", + __func__, chip->reg_ns1_q0); + + for (output = 1; output <= 3; output++) { + struct clk_register_offsets offsets; + + err = idt24x_get_offsets(output, &offsets); + if (err) { + dev_err(&client->dev, + "%s: error calling idt24x_get_offsets: %i", + __func__, err); + return err; + } + + err = regmap_read(chip->regmap, offsets.n_17_16_offset, + &chip->reg_n_qx_17_16[output - 1]); + if (err) { + dev_err(&client->dev, + "%s: error reading n_17_16_offset for output %d (offset: 0x%x): %i", + __func__, output, offsets.n_17_16_offset, err); + return err; + } + dev_dbg(&client->dev, + "%s: reg_n_qx_17_16[Q%u]: 0x%x", + __func__, output, chip->reg_n_qx_17_16[output - 1]); + + err = regmap_read(chip->regmap, offsets.nfrac_27_24_offset, + &chip->reg_nfrac_qx_27_24[output - 1]); + if (err) { + dev_err(&client->dev, + "%s: error reading nfrac_27_24_offset for output %d (offset: 0x%x): %i", + __func__, output, + offsets.nfrac_27_24_offset, err); + return err; + } + dev_dbg(&client->dev, + "%s: reg_nfrac_qx_27_24[Q%u]: 0x%x", + __func__, output, + chip->reg_nfrac_qx_27_24[output - 1]); + } + + dev_info(&client->dev, + "%s: initial values read from chip successfully", + __func__); + + /* Also read DBL_DIS to determine whether the doubler is disabled. */ + err = regmap_read(chip->regmap, IDT24x_REG_DBL_DIS, &tmp); + if (err) { + dev_err(&client->dev, + "%s: error reading IDT24x_REG_DBL_DIS: %i", + __func__, err); + return err; + } + chip->doubler_disabled = mask_and_shift(tmp, IDT24x_REG_DBL_DIS_MASK); + dev_dbg(&client->dev, "%s: doubler_disabled: %d", + __func__, chip->doubler_disabled); + + return 0; +} + +/** + * idt24x_set_rate - Sets the specified output clock to the specified rate. + * @hw: clk_hw struct that identifies the specific output clock. + * @rate: the rate (in Hz) for the specified clock. + * @parent_rate:(not sure) the rate for a parent signal (e.g., + * the VCO feeding the output) + * + * This function will call idt24_set_frequency, which means it will + * calculate divider for all requested outputs and update the attached + * device (issue I2C commands to update the registers). + * + * Return: 0 on success. + */ +static int idt24x_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int err = 0; + + /* + * hw->clk is the pointer to the specific output clock the user is + * requesting. We use hw to get back to the output structure for + * the output clock. Set the requested rate in the output structure. + * Note that container_of cannot be used to find the device structure + * (clk_idt24x_chip) from clk_hw, because clk_idt24x_chip has an array + * of idt24x_output structs. That is why it is necessary to use + * output->chip to access the device structure. + */ + struct idt24x_output *output = to_idt24x_output(hw); + struct i2c_client *client = output->chip->i2c_client; + + if (rate < output->chip->min_freq || rate > output->chip->max_freq) { + dev_err(&client->dev, + "requested frequency (%luHz) is out of range\n", rate); + return -EINVAL; + } + + /* + * Set the requested frequency in the output data structure, and then + * call idt24x_set_frequency. idt24x_set_frequency considers all + * requested frequencies when deciding on a vco frequency and + * calculating dividers. + */ + output->requested = rate; + + /* + * Also set in the memory location used by the debugfs file + * that exposes the output clock frequency. That allows querying + * the current rate via debugfs. + */ + output->debug_freq = rate; + + dev_info(&client->dev, + "%s. calling idt24x_set_frequency for Q%u. rate: %lu", + __func__, output->index, rate); + err = idt24x_set_frequency(output->chip); + + if (err != 0) + dev_err(&client->dev, "error calling set_frequency: %d", err); + + return err; +} + +/** + * idt24x_round_rate - get valid rate that is closest to the requested rate + * @hw: clk_hw struct that identifies the specific output clock. + * @rate: the rate (in Hz) for the specified clock. + * @parent_rate:(not sure) the rate for a parent signal (e.g., the VCO + * feeding the output). This is an i/o param. + * If the driver supports a parent clock for the output (e.g., + * the VCO(?), then set this param to indicate what the rate of + * the parent would be (e.g., the VCO frequency) if the rounded + * rate is used. + * + * Returns the closest rate to the requested rate actually supported by the + * chip. + * + * Return: adjusted rate + */ +static long idt24x_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + /* + * The chip has fractional output dividers, so assume it + * can provide the requested rate. + * + * TODO: figure out the closest rate that chip can support + * within a low error threshold and return that rate. + */ + return rate; +} + +/** + * idt24x_recalc_rate - return the frequency being provided by the clock. + * @hw: clk_hw struct that identifies the specific output clock. + * @parent_rate: (not sure) the rate for a parent signal (e.g., the + * VCO feeding the output) + * + * This API appears to be used to read the current values from the hardware + * and report the frequency being provided by the clock. Without this function, + * the clock will be initialized to 0 by default. The OS appears to be + * calling this to find out what the current value of the clock is at + * startup, so it can determine when .set_rate is actually changing the + * frequency. + * + * Return: the frequency of the specified clock. + */ +static unsigned long idt24x_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct idt24x_output *output = to_idt24x_output(hw); + + return output->requested; +} + +/* + * Note that .prepare and .unprepare appear to be used more in Gates. + * They do not appear to be necessary for this device. + * Instead, update the device when .set_rate is called. + */ +static const struct clk_ops idt24x_clk_ops = { + .recalc_rate = idt24x_recalc_rate, + .round_rate = idt24x_round_rate, + .set_rate = idt24x_set_rate, +}; + +static bool idt24x_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return false; +} + +static bool idt24x_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + return true; +} + +static const struct regmap_config idt24x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 0xff, + .writeable_reg = idt24x_regmap_is_writeable, + .volatile_reg = idt24x_regmap_is_volatile, +}; + +/** + * idt24x_clk_notifier_cb - Clock rate change callback + * @nb: Pointer to notifier block + * @event: Notification reason + * @data: Pointer to notification data object + * + * This function is called when the input clock frequency changes. + * The callback checks whether a valid bus frequency can be generated after the + * change. If so, the change is acknowledged, otherwise the change is aborted. + * New dividers are written to the HW in the pre- or post change notification + * depending on the scaling direction. + * + * Return: NOTIFY_STOP if the rate change should be aborted, NOTIFY_OK + * to acknowledge the change, NOTIFY_DONE if the notification is + * considered irrelevant. + */ +static int idt24x_clk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct clk_idt24x_chip *chip = to_clk_idt24x_from_nb(nb); + int err = 0; + + dev_info(&chip->i2c_client->dev, + "%s: input frequency changed: %lu Hz. event: %lu", + __func__, ndata->new_rate, event); + + switch (event) { + case PRE_RATE_CHANGE: { + dev_dbg(&chip->i2c_client->dev, "PRE_RATE_CHANGE\n"); + return NOTIFY_OK; + } + case POST_RATE_CHANGE: + chip->input_clk_freq = ndata->new_rate; + /* + * Can't call clock API clk_set_rate here; I believe + * it will be ignored if the rate is the same as we + * set previously. Need to call our internal function. + */ + dev_dbg(&chip->i2c_client->dev, + "POST_RATE_CHANGE. Calling idt24x_set_frequency\n"); + err = idt24x_set_frequency(chip); + if (err) + dev_err(&chip->i2c_client->dev, + "error calling idt24x_set_frequency (%i)\n", + err); + return NOTIFY_OK; + case ABORT_RATE_CHANGE: + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static struct clk_hw *of_clk_idt24x_get( + struct of_phandle_args *clkspec, void *_data) +{ + struct clk_idt24x_chip *chip = _data; + unsigned int idx = clkspec->args[0]; + + if (idx >= ARRAY_SIZE(chip->clk)) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &chip->clk[idx].hw; +} + +/** + * idt24x_probe - main entry point for ccf driver + * @client: pointer to i2c_client structure + * @id: pointer to i2c_device_id structure + * + * Main entry point function that gets called to initialize the driver. + * + * Return: 0 for success. + */ +static int idt24x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct clk_idt24x_chip *chip; + struct clk_init_data init; + + int err = 0; + int x; + char buf[6]; + + dev_info(&client->dev, "%s", __func__); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + init.ops = &idt24x_clk_ops; + init.flags = 0; + init.num_parents = 0; + chip->i2c_client = client; + + chip->min_freq = IDT24x_MIN_FREQ; + chip->max_freq = IDT24x_MAX_FREQ; + + for (x = 0; x < NUM_INPUTS + 1; x++) { + char name[12]; + + sprintf(name, x == NUM_INPUTS ? "input-xtal" : "input-clk%i", + x); + dev_dbg(&client->dev, "attempting to get %s", name); + chip->input_clk = devm_clk_get(&client->dev, name); + if (IS_ERR(chip->input_clk)) { + err = PTR_ERR(chip->input_clk); + /* + * TODO: Handle EPROBE_DEFER error, which indicates + * that the input_clk isn't available now but may be + * later when the appropriate module is loaded. + */ + } else { + err = 0; + chip->input_clk_num = x; + break; + } + } + + if (err) { + dev_err(&client->dev, "Unable to get input clock (%u).", err); + chip->input_clk = NULL; + return err; + } + + chip->input_clk_freq = clk_get_rate(chip->input_clk); + dev_dbg(&client->dev, "Got input-freq from input-clk in device tree: %uHz", + chip->input_clk_freq); + + chip->input_clk_nb.notifier_call = idt24x_clk_notifier_cb; + if (clk_notifier_register(chip->input_clk, &chip->input_clk_nb)) + dev_warn(&client->dev, + "Unable to register clock notifier for input_clk."); + + dev_dbg(&client->dev, "%s: about to read settings: %zu", + __func__, ARRAY_SIZE(chip->settings)); + + err = of_property_read_u8_array( + client->dev.of_node, "settings", chip->settings, + ARRAY_SIZE(chip->settings)); + if (!err) { + dev_dbg(&client->dev, "settings property specified in DT"); + chip->has_settings = true; + } else { + if (err == -EOVERFLOW) { + dev_alert(&client->dev, + "EOVERFLOW error trying to read the settings. ARRAY_SIZE: %zu", + ARRAY_SIZE(chip->settings)); + return err; + } + dev_dbg(&client->dev, + "settings property not specified in DT (or there was an error that can be ignored: %i). The settings property is optional.", + err); + } + + /* + * Requested output frequencies cannot be specified in the DT. + * Either a consumer needs to use the clock API to request the rate, + * or use debugfs to set the rate from user space. Use clock-names in + * DT to specify the output clock. + */ + + chip->regmap = devm_regmap_init_i2c(client, &idt24x_regmap_config); + if (IS_ERR(chip->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(chip->regmap); + } + + dev_dbg(&client->dev, "%s: call i2c_set_clientdata", __func__); + i2c_set_clientdata(client, chip); + + if (chip->has_settings) { + /* + * A raw settings array was specified in the DT. Write the + * settings to the device immediately. + */ + err = i2cwritebulk( + chip->i2c_client, chip->regmap, 0, chip->settings, + ARRAY_SIZE(chip->settings)); + if (err) { + dev_err(&client->dev, + "error writing all settings to chip (%i)\n", + err); + return err; + } + dev_dbg(&client->dev, "successfully wrote full settings array"); + } + + /* + * Whether or not settings were written to the device, read all + * current values from the hw. + */ + dev_dbg(&client->dev, "read from HW"); + err = idt24x_read_from_hw(chip); + if (err) { + dev_err(&client->dev, + "failed calling idt24x_read_from_hw (%i)\n", err); + return err; + } + + /* Create all 4 clocks */ + for (x = 0; x < NUM_OUTPUTS; x++) { + init.name = kasprintf( + GFP_KERNEL, "%s.Q%i", client->dev.of_node->name, x); + chip->clk[x].chip = chip; + chip->clk[x].hw.init = &init; + chip->clk[x].index = x; + err = devm_clk_hw_register(&client->dev, &chip->clk[x].hw); + kfree(init.name); /* clock framework made a copy of the name */ + if (err) { + dev_err(&client->dev, "clock registration failed\n"); + return err; + } + dev_dbg(&client->dev, "successfully registered Q%i", x); + } + + if (err) { + dev_err(&client->dev, "clock registration failed\n"); + return err; + } + + err = of_clk_add_hw_provider( + client->dev.of_node, of_clk_idt24x_get, chip); + if (err) { + dev_err(&client->dev, "unable to add clk provider\n"); + return err; + } + + err = idt24x_expose_via_debugfs(client, chip); + if (err) { + dev_err(&client->dev, + "error calling idt24x_expose_via_debugfs: %i\n", err); + return err; + } + + if (chip->input_clk_num == NUM_INPUTS) + sprintf(buf, "XTAL"); + else + sprintf(buf, "CLK%i", chip->input_clk_num); + dev_info(&client->dev, "probe success. input freq: %uHz (%s), settings string? %s\n", + chip->input_clk_freq, buf, + chip->has_settings ? "true" : "false"); + return 0; +} + +static int idt24x_remove(struct i2c_client *client) +{ + struct clk_idt24x_chip *chip = to_clk_idt24x_from_client(&client); + + dev_info(&client->dev, "%s", __func__); + of_clk_del_provider(client->dev.of_node); + idt24x_cleanup_debugfs(chip); + + if (!chip->input_clk) + clk_notifier_unregister( + chip->input_clk, &chip->input_clk_nb); + return 0; +} + +static const struct i2c_device_id idt24x_id[] = { + { "idt8t49n24x", idt24x }, + { } +}; +MODULE_DEVICE_TABLE(i2c, idt24x_id); + +static const struct of_device_id idt24x_of_match[] = { + { .compatible = "idt,idt8t49n241" }, + {}, +}; +MODULE_DEVICE_TABLE(of, idt24x_of_match); + +static struct i2c_driver idt24x_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = idt24x_of_match, + }, + .probe = idt24x_probe, + .remove = idt24x_remove, + .id_table = idt24x_id, +}; + +module_i2c_driver(idt24x_driver); + +MODULE_DESCRIPTION("8T49N24x ccf driver"); +MODULE_AUTHOR("David Cater <david.cater@idt.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index 1ac0c7990392..087170a9b148 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -30,5 +30,6 @@ config CLK_IMX8QXP bool "IMX8QXP SCU Clock" depends on ARCH_MXC && IMX_SCU && ARM64 select MXC_CLK_SCU + select MXC_CLK help Build the driver for IMX8QXP SCU based clocks. diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index d3486ee79ab5..78122188ac39 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -95,7 +95,7 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, int prediv_value; int div_value; int ret; - u32 val; + u32 orig, val; ret = imx8m_clk_composite_compute_dividers(rate, parent_rate, &prediv_value, &div_value); @@ -104,13 +104,15 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, spin_lock_irqsave(divider->lock, flags); - val = readl(divider->reg); - val &= ~((clk_div_mask(divider->width) << divider->shift) | - (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT)); + orig = readl(divider->reg); + val = orig & ~((clk_div_mask(divider->width) << divider->shift) | + (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT)); val |= (u32)(prediv_value - 1) << divider->shift; val |= (u32)(div_value - 1) << PCG_DIV_SHIFT; - writel(val, divider->reg); + + if (val != orig) + writel(val, divider->reg); spin_unlock_irqrestore(divider->lock, flags); diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index c4685c01929a..579b638b09b8 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -287,13 +287,13 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) hws[IMX6SX_CLK_SSI3_SEL] = imx_clk_hw_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); hws[IMX6SX_CLK_SSI2_SEL] = imx_clk_hw_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); hws[IMX6SX_CLK_SSI1_SEL] = imx_clk_hw_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); - hws[IMX6SX_CLK_QSPI1_SEL] = imx_clk_hw_mux_flags("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_CLK_QSPI1_SEL] = imx_clk_hw_mux("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels)); hws[IMX6SX_CLK_PERCLK_SEL] = imx_clk_hw_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); hws[IMX6SX_CLK_VID_SEL] = imx_clk_hw_mux("vid_sel", base + 0x20, 21, 3, vid_sels, ARRAY_SIZE(vid_sels)); hws[IMX6SX_CLK_ESAI_SEL] = imx_clk_hw_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); hws[IMX6SX_CLK_CAN_SEL] = imx_clk_hw_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels)); hws[IMX6SX_CLK_UART_SEL] = imx_clk_hw_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); - hws[IMX6SX_CLK_QSPI2_SEL] = imx_clk_hw_mux_flags("qspi2_sel", base + 0x2c, 15, 3, qspi2_sels, ARRAY_SIZE(qspi2_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_CLK_QSPI2_SEL] = imx_clk_hw_mux("qspi2_sel", base + 0x2c, 15, 3, qspi2_sels, ARRAY_SIZE(qspi2_sels)); hws[IMX6SX_CLK_SPDIF_SEL] = imx_clk_hw_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); hws[IMX6SX_CLK_AUDIO_SEL] = imx_clk_hw_mux("audio_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); hws[IMX6SX_CLK_ENET_PRE_SEL] = imx_clk_hw_mux("enet_pre_sel", base + 0x34, 15, 3, enet_pre_sels, ARRAY_SIZE(enet_pre_sels)); diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 172589e94f60..ec34c5241636 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -26,73 +26,6 @@ static u32 share_count_disp; static u32 share_count_pdm; static u32 share_count_nand; -static const struct imx_pll14xx_rate_table imx8mm_pll1416x_tbl[] = { - PLL_1416X_RATE(1800000000U, 225, 3, 0), - PLL_1416X_RATE(1600000000U, 200, 3, 0), - PLL_1416X_RATE(1200000000U, 300, 3, 1), - PLL_1416X_RATE(1000000000U, 250, 3, 1), - PLL_1416X_RATE(800000000U, 200, 3, 1), - PLL_1416X_RATE(750000000U, 250, 2, 2), - PLL_1416X_RATE(700000000U, 350, 3, 2), - PLL_1416X_RATE(600000000U, 300, 3, 2), -}; - -static const struct imx_pll14xx_rate_table imx8mm_audiopll_tbl[] = { - PLL_1443X_RATE(393216000U, 262, 2, 3, 9437), - PLL_1443X_RATE(361267200U, 361, 3, 3, 17511), -}; - -static const struct imx_pll14xx_rate_table imx8mm_videopll_tbl[] = { - PLL_1443X_RATE(650000000U, 325, 3, 2, 0), - PLL_1443X_RATE(594000000U, 198, 2, 2, 0), -}; - -static const struct imx_pll14xx_rate_table imx8mm_drampll_tbl[] = { - PLL_1443X_RATE(650000000U, 325, 3, 2, 0), -}; - -static struct imx_pll14xx_clk imx8mm_audio_pll = { - .type = PLL_1443X, - .rate_table = imx8mm_audiopll_tbl, - .rate_count = ARRAY_SIZE(imx8mm_audiopll_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_video_pll = { - .type = PLL_1443X, - .rate_table = imx8mm_videopll_tbl, - .rate_count = ARRAY_SIZE(imx8mm_videopll_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_dram_pll = { - .type = PLL_1443X, - .rate_table = imx8mm_drampll_tbl, - .rate_count = ARRAY_SIZE(imx8mm_drampll_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_arm_pll = { - .type = PLL_1416X, - .rate_table = imx8mm_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_gpu_pll = { - .type = PLL_1416X, - .rate_table = imx8mm_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_vpu_pll = { - .type = PLL_1416X, - .rate_table = imx8mm_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_sys_pll = { - .type = PLL_1416X, - .rate_table = imx8mm_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), -}; - static const char *pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; @@ -396,16 +329,16 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) clks[IMX8MM_SYS_PLL2_REF_SEL] = imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MM_SYS_PLL3_REF_SEL] = imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MM_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx8mm_audio_pll); - clks[IMX8MM_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx8mm_audio_pll); - clks[IMX8MM_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx8mm_video_pll); - clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx8mm_dram_pll); - clks[IMX8MM_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx8mm_gpu_pll); - clks[IMX8MM_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx8mm_vpu_pll); - clks[IMX8MM_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx8mm_arm_pll); - clks[IMX8MM_SYS_PLL1] = imx_clk_pll14xx("sys_pll1", "sys_pll1_ref_sel", base + 0x94, &imx8mm_sys_pll); - clks[IMX8MM_SYS_PLL2] = imx_clk_pll14xx("sys_pll2", "sys_pll2_ref_sel", base + 0x104, &imx8mm_sys_pll); - clks[IMX8MM_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx8mm_sys_pll); + clks[IMX8MM_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx_1443x_pll); + clks[IMX8MM_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx_1443x_pll); + clks[IMX8MM_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx_1443x_pll); + clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_pll); + clks[IMX8MM_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx_1416x_pll); + clks[IMX8MM_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx_1416x_pll); + clks[IMX8MM_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx_1416x_pll); + clks[IMX8MM_SYS_PLL1] = imx_clk_pll14xx("sys_pll1", "sys_pll1_ref_sel", base + 0x94, &imx_1416x_pll); + clks[IMX8MM_SYS_PLL2] = imx_clk_pll14xx("sys_pll2", "sys_pll2_ref_sel", base + 0x104, &imx_1416x_pll); + clks[IMX8MM_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll); /* PLL bypass out */ clks[IMX8MM_AUDIO_PLL1_BYPASS] = imx_clk_mux_flags("audio_pll1_bypass", base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index 882b42efd258..9d33321c89bd 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -189,27 +189,27 @@ static const char * const imx8mn_disp_pixel_sels[] = {"osc_24m", "video_pll1_out "sys_pll3_out", "clk_ext4", }; static const char * const imx8mn_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", - "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "video_pll1_out", "sys_pll1_133m", "dummy", "clk_ext3", "clk_ext4", }; static const char * const imx8mn_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", - "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "video_pll1_out", "sys_pll1_133m", "dummy", "clk_ext3", "clk_ext4", }; static const char * const imx8mn_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", - "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "video_pll1_out", "sys_pll1_133m", "dummy", "clk_ext2", "clk_ext3", }; static const char * const imx8mn_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", - "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "video_pll1_out", "sys_pll1_133m", "dummy", "clk_ext3", "clk_ext4", }; static const char * const imx8mn_sai7_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", - "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "video_pll1_out", "sys_pll1_133m", "dummy", "clk_ext3", "clk_ext4", }; static const char * const imx8mn_spdif1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", - "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "video_pll1_out", "sys_pll1_133m", "dummy", "clk_ext2", "clk_ext3", }; static const char * const imx8mn_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", @@ -582,7 +582,7 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) clks[IMX8MN_CLK_UART2_ROOT] = imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0); clks[IMX8MN_CLK_UART3_ROOT] = imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0); clks[IMX8MN_CLK_UART4_ROOT] = imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0); - clks[IMX8MN_CLK_USB1_CTRL_ROOT] = imx_clk_gate4("usb1_ctrl_root_clk", "usb_core_ref", base + 0x44d0, 0); + clks[IMX8MN_CLK_USB1_CTRL_ROOT] = imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0); clks[IMX8MN_CLK_GPU_CORE_ROOT] = imx_clk_gate4("gpu_core_root_clk", "gpu_core_div", base + 0x44f0, 0); clks[IMX8MN_CLK_USDHC1_ROOT] = imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0); clks[IMX8MN_CLK_USDHC2_ROOT] = imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0); diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index 047f1d8fe323..c43e9653b415 100644 --- a/drivers/clk/imx/clk-pll14xx.c +++ b/drivers/clk/imx/clk-pll14xx.c @@ -41,6 +41,36 @@ struct clk_pll14xx { #define to_clk_pll14xx(_hw) container_of(_hw, struct clk_pll14xx, hw) +const struct imx_pll14xx_rate_table imx_pll1416x_tbl[] = { + PLL_1416X_RATE(1800000000U, 225, 3, 0), + PLL_1416X_RATE(1600000000U, 200, 3, 0), + PLL_1416X_RATE(1200000000U, 300, 3, 1), + PLL_1416X_RATE(1000000000U, 250, 3, 1), + PLL_1416X_RATE(800000000U, 200, 3, 1), + PLL_1416X_RATE(750000000U, 250, 2, 2), + PLL_1416X_RATE(700000000U, 350, 3, 2), + PLL_1416X_RATE(600000000U, 300, 3, 2), +}; + +const struct imx_pll14xx_rate_table imx_pll1443x_tbl[] = { + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), + PLL_1443X_RATE(594000000U, 198, 2, 2, 0), + PLL_1443X_RATE(393216000U, 262, 2, 3, 9437), + PLL_1443X_RATE(361267200U, 361, 3, 3, 17511), +}; + +struct imx_pll14xx_clk imx_1443x_pll = { + .type = PLL_1443X, + .rate_table = imx_pll1443x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1443x_tbl), +}; + +struct imx_pll14xx_clk imx_1416x_pll = { + .type = PLL_1416X, + .rate_table = imx_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1416x_tbl), +}; + static const struct imx_pll14xx_rate_table *imx_get_pll_settings( struct clk_pll14xx *pll, unsigned long rate) { diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 6fe64ff8ffa1..30ddbc1ced2e 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -50,6 +50,9 @@ struct imx_pll14xx_clk { int flags; }; +extern struct imx_pll14xx_clk imx_1416x_pll; +extern struct imx_pll14xx_clk imx_1443x_pll; + #define imx_clk_cpu(name, parent_name, div, mux, pll, step) \ to_clk(imx_clk_hw_cpu(name, parent_name, div, mux, pll, step)) diff --git a/drivers/clk/ingenic/tcu.c b/drivers/clk/ingenic/tcu.c index 926696fba3f4..d8739871e114 100644 --- a/drivers/clk/ingenic/tcu.c +++ b/drivers/clk/ingenic/tcu.c @@ -100,15 +100,11 @@ static bool ingenic_tcu_enable_regs(struct clk_hw *hw) bool enabled = false; /* - * If the SoC has no global TCU clock, we must ungate the channel's - * clock to be able to access its registers. - * If we have a TCU clock, it will be enabled automatically as it has - * been attached to the regmap. + * According to the programming manual, a timer channel's registers can + * only be accessed when the channel's stop bit is clear. */ - if (!tcu->clk) { - enabled = !!ingenic_tcu_is_enabled(hw); - regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit)); - } + enabled = !!ingenic_tcu_is_enabled(hw); + regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit)); return enabled; } @@ -119,8 +115,7 @@ static void ingenic_tcu_disable_regs(struct clk_hw *hw) const struct ingenic_tcu_clk_info *info = tcu_clk->info; struct ingenic_tcu *tcu = tcu_clk->tcu; - if (!tcu->clk) - regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit)); + regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit)); } static u8 ingenic_tcu_get_parent(struct clk_hw *hw) diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c index d59a7621bb20..6bbdd4705d71 100644 --- a/drivers/clk/keystone/pll.c +++ b/drivers/clk/keystone/pll.c @@ -209,7 +209,7 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) } clk = clk_register_pll(NULL, node->name, parent_name, pll_data); - if (clk) { + if (!IS_ERR_OR_NULL(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); return; } @@ -281,12 +281,13 @@ static void __init of_pll_div_clk_init(struct device_node *node) clk = clk_register_divider(NULL, clk_name, parent_name, 0, reg, shift, mask, 0, NULL); - if (clk) { - of_clk_add_provider(node, of_clk_src_simple_get, clk); - } else { + if (IS_ERR(clk)) { pr_err("%s: error registering divider %s\n", __func__, clk_name); iounmap(reg); + return; } + + of_clk_add_provider(node, of_clk_src_simple_get, clk); } CLK_OF_DECLARE(pll_divider_clock, "ti,keystone,pll-divider-clock", of_pll_div_clk_init); @@ -328,10 +329,12 @@ static void __init of_pll_mux_clk_init(struct device_node *node) clk = clk_register_mux(NULL, clk_name, (const char **)&parents, ARRAY_SIZE(parents) , 0, reg, shift, mask, 0, NULL); - if (clk) - of_clk_add_provider(node, of_clk_src_simple_get, clk); - else + if (IS_ERR(clk)) { pr_err("%s: error registering mux %s\n", __func__, clk_name); + return; + } + + of_clk_add_provider(node, of_clk_src_simple_get, clk); } CLK_OF_DECLARE(pll_mux_clock, "ti,keystone,pll-mux-clock", of_pll_mux_clk_init); diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index 64ea895f1a7d..8e28e3489ded 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -287,6 +287,8 @@ static int _sci_clk_build(struct sci_clk_provider *provider, name = kasprintf(GFP_KERNEL, "clk:%d:%d", sci_clk->dev_id, sci_clk->clk_id); + if (!name) + return -ENOMEM; init.name = name; diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c index 695be0f77427..c67cd73aca17 100644 --- a/drivers/clk/mediatek/clk-mt2701.c +++ b/drivers/clk/mediatek/clk-mt2701.c @@ -675,6 +675,8 @@ static int mtk_topckgen_init(struct platform_device *pdev) return PTR_ERR(base); clk_data = mtk_alloc_clk_data(CLK_TOP_NR); + if (!clk_data) + return -ENOMEM; mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), clk_data); @@ -742,6 +744,8 @@ static void __init mtk_infrasys_init_early(struct device_node *node) if (!infra_clk_data) { infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR); + if (!infra_clk_data) + return; for (i = 0; i < CLK_INFRA_NR; i++) infra_clk_data->clks[i] = ERR_PTR(-EPROBE_DEFER); @@ -768,6 +772,8 @@ static int mtk_infrasys_init(struct platform_device *pdev) if (!infra_clk_data) { infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR); + if (!infra_clk_data) + return -ENOMEM; } else { for (i = 0; i < CLK_INFRA_NR; i++) { if (infra_clk_data->clks[i] == ERR_PTR(-EPROBE_DEFER)) @@ -896,6 +902,8 @@ static int mtk_pericfg_init(struct platform_device *pdev) return PTR_ERR(base); clk_data = mtk_alloc_clk_data(CLK_PERI_NR); + if (!clk_data) + return -ENOMEM; mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks), clk_data); diff --git a/drivers/clk/mediatek/clk-mt6779.c b/drivers/clk/mediatek/clk-mt6779.c index 00920182bbe6..f7b5ec749ab9 100644 --- a/drivers/clk/mediatek/clk-mt6779.c +++ b/drivers/clk/mediatek/clk-mt6779.c @@ -1216,6 +1216,8 @@ static int clk_mt6779_apmixed_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); + if (!clk_data) + return -ENOMEM; mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); @@ -1237,6 +1239,8 @@ static int clk_mt6779_top_probe(struct platform_device *pdev) return PTR_ERR(base); clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + if (!clk_data) + return -ENOMEM; mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), clk_data); diff --git a/drivers/clk/mediatek/clk-mt6797.c b/drivers/clk/mediatek/clk-mt6797.c index f62b0428da0e..9bef47161bde 100644 --- a/drivers/clk/mediatek/clk-mt6797.c +++ b/drivers/clk/mediatek/clk-mt6797.c @@ -392,6 +392,8 @@ static int mtk_topckgen_init(struct platform_device *pdev) return PTR_ERR(base); clk_data = mtk_alloc_clk_data(CLK_TOP_NR); + if (!clk_data) + return -ENOMEM; mtk_clk_register_factors(top_fixed_divs, ARRAY_SIZE(top_fixed_divs), clk_data); @@ -564,6 +566,8 @@ static void mtk_infrasys_init_early(struct device_node *node) if (!infra_clk_data) { infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR); + if (!infra_clk_data) + return; for (i = 0; i < CLK_INFRA_NR; i++) infra_clk_data->clks[i] = ERR_PTR(-EPROBE_DEFER); @@ -588,6 +592,8 @@ static int mtk_infrasys_init(struct platform_device *pdev) if (!infra_clk_data) { infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR); + if (!infra_clk_data) + return -ENOMEM; } else { for (i = 0; i < CLK_INFRA_NR; i++) { if (infra_clk_data->clks[i] == ERR_PTR(-EPROBE_DEFER)) diff --git a/drivers/clk/mediatek/clk-mt7629-eth.c b/drivers/clk/mediatek/clk-mt7629-eth.c index 88279d0ea1a7..3ab7b672f8c7 100644 --- a/drivers/clk/mediatek/clk-mt7629-eth.c +++ b/drivers/clk/mediatek/clk-mt7629-eth.c @@ -83,6 +83,8 @@ static int clk_mt7629_ethsys_init(struct platform_device *pdev) int r; clk_data = mtk_alloc_clk_data(CLK_ETH_NR_CLK); + if (!clk_data) + return -ENOMEM; mtk_clk_register_gates(node, eth_clks, CLK_ETH_NR_CLK, clk_data); @@ -105,6 +107,8 @@ static int clk_mt7629_sgmiisys_init(struct platform_device *pdev) int r; clk_data = mtk_alloc_clk_data(CLK_SGMII_NR_CLK); + if (!clk_data) + return -ENOMEM; mtk_clk_register_gates(node, sgmii_clks[id++], CLK_SGMII_NR_CLK, clk_data); diff --git a/drivers/clk/mediatek/clk-mt7629.c b/drivers/clk/mediatek/clk-mt7629.c index d6233994af5a..1e6abbddf02c 100644 --- a/drivers/clk/mediatek/clk-mt7629.c +++ b/drivers/clk/mediatek/clk-mt7629.c @@ -581,6 +581,8 @@ static int mtk_topckgen_init(struct platform_device *pdev) return PTR_ERR(base); clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + if (!clk_data) + return -ENOMEM; mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks), clk_data); @@ -605,6 +607,8 @@ static int mtk_infrasys_init(struct platform_device *pdev) int r; clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + if (!clk_data) + return -ENOMEM; mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), clk_data); @@ -633,6 +637,8 @@ static int mtk_pericfg_init(struct platform_device *pdev) return PTR_ERR(base); clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + if (!clk_data) + return -ENOMEM; mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks), clk_data); diff --git a/drivers/clk/mediatek/clk-mt8183-mfgcfg.c b/drivers/clk/mediatek/clk-mt8183-mfgcfg.c index 37b4162c5882..3a33014eee7f 100644 --- a/drivers/clk/mediatek/clk-mt8183-mfgcfg.c +++ b/drivers/clk/mediatek/clk-mt8183-mfgcfg.c @@ -18,9 +18,9 @@ static const struct mtk_gate_regs mfg_cg_regs = { .sta_ofs = 0x0, }; -#define GATE_MFG(_id, _name, _parent, _shift) \ - GATE_MTK(_id, _name, _parent, &mfg_cg_regs, _shift, \ - &mtk_clk_gate_ops_setclr) +#define GATE_MFG(_id, _name, _parent, _shift) \ + GATE_MTK_FLAGS(_id, _name, _parent, &mfg_cg_regs, _shift, \ + &mtk_clk_gate_ops_setclr, CLK_SET_RATE_PARENT) static const struct mtk_gate mfg_clks[] = { GATE_MFG(CLK_MFG_BG3D, "mfg_bg3d", "mfg_sel", 0) diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c index cb939c071b0c..89916acf0bc3 100644 --- a/drivers/clk/mediatek/reset.c +++ b/drivers/clk/mediatek/reset.c @@ -25,7 +25,7 @@ static int mtk_reset_assert_set_clr(struct reset_controller_dev *rcdev, struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); unsigned int reg = data->regofs + ((id / 32) << 4); - return regmap_write(data->regmap, reg, 1); + return regmap_write(data->regmap, reg, BIT(id % 32)); } static int mtk_reset_deassert_set_clr(struct reset_controller_dev *rcdev, @@ -34,7 +34,7 @@ static int mtk_reset_deassert_set_clr(struct reset_controller_dev *rcdev, struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); unsigned int reg = data->regofs + ((id / 32) << 4) + 0x4; - return regmap_write(data->regmap, reg, 1); + return regmap_write(data->regmap, reg, BIT(id % 32)); } static int mtk_reset_assert(struct reset_controller_dev *rcdev, diff --git a/drivers/clk/meson/meson-aoclk.c b/drivers/clk/meson/meson-aoclk.c index bf8bea675d24..6c7110a96194 100644 --- a/drivers/clk/meson/meson-aoclk.c +++ b/drivers/clk/meson/meson-aoclk.c @@ -36,6 +36,7 @@ int meson_aoclkc_probe(struct platform_device *pdev) struct meson_aoclk_reset_controller *rstc; struct meson_aoclk_data *data; struct device *dev = &pdev->dev; + struct device_node *np; struct regmap *regmap; int ret, clkid; @@ -47,7 +48,9 @@ int meson_aoclkc_probe(struct platform_device *pdev) if (!rstc) return -ENOMEM; - regmap = syscon_node_to_regmap(of_get_parent(dev->of_node)); + np = of_get_parent(dev->of_node); + regmap = syscon_node_to_regmap(np); + of_node_put(np); if (IS_ERR(regmap)) { dev_err(dev, "failed to get regmap\n"); return PTR_ERR(regmap); diff --git a/drivers/clk/meson/meson-eeclk.c b/drivers/clk/meson/meson-eeclk.c index a7cb1e7aedc4..18ae38787268 100644 --- a/drivers/clk/meson/meson-eeclk.c +++ b/drivers/clk/meson/meson-eeclk.c @@ -17,6 +17,7 @@ int meson_eeclkc_probe(struct platform_device *pdev) { const struct meson_eeclkc_data *data; struct device *dev = &pdev->dev; + struct device_node *np; struct regmap *map; int ret, i; @@ -25,7 +26,9 @@ int meson_eeclkc_probe(struct platform_device *pdev) return -EINVAL; /* Get the hhi system controller node */ - map = syscon_node_to_regmap(of_get_parent(dev->of_node)); + np = of_get_parent(dev->of_node); + map = syscon_node_to_regmap(np); + of_node_put(np); if (IS_ERR(map)) { dev_err(dev, "failed to get HHI regmap\n"); diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index 082178a0f41a..efddf0d152a4 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -3684,13 +3684,16 @@ static void __init meson8b_clkc_init_common(struct device_node *np, struct clk_hw_onecell_data *clk_hw_onecell_data) { struct meson8b_clk_reset *rstc; + struct device_node *parent_np; const char *notifier_clk_name; struct clk *notifier_clk; void __iomem *clk_base; struct regmap *map; int i, ret; - map = syscon_node_to_regmap(of_get_parent(np)); + parent_np = of_get_parent(np); + map = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); if (IS_ERR(map)) { pr_info("failed to get HHI regmap - Trying obsolete regs\n"); diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index f110c02e83cb..9674c6c06dca 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -258,18 +258,21 @@ static void __init pxa168_clk_init(struct device_node *np) pxa_unit->mpmu_base = of_iomap(np, 0); if (!pxa_unit->mpmu_base) { pr_err("failed to map mpmu registers\n"); + kfree(pxa_unit); return; } pxa_unit->apmu_base = of_iomap(np, 1); if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); + kfree(pxa_unit); return; } pxa_unit->apbc_base = of_iomap(np, 2); if (!pxa_unit->apbc_base) { pr_err("failed to map apbc registers\n"); + kfree(pxa_unit); return; } diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c index 1b2cefef7431..a8a2cfa83290 100644 --- a/drivers/clk/qcom/camcc-sdm845.c +++ b/drivers/clk/qcom/camcc-sdm845.c @@ -1521,6 +1521,8 @@ static struct clk_branch cam_cc_sys_tmr_clk = { }, }; +static struct gdsc titan_top_gdsc; + static struct gdsc bps_gdsc = { .gdscr = 0x6004, .pd = { @@ -1554,6 +1556,7 @@ static struct gdsc ife_0_gdsc = { .name = "ife_0_gdsc", }, .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, .pwrsts = PWRSTS_OFF_ON, }; @@ -1563,6 +1566,7 @@ static struct gdsc ife_1_gdsc = { .name = "ife_1_gdsc", }, .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, .pwrsts = PWRSTS_OFF_ON, }; diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c index 59f1af415b58..e74fc81a14d0 100644 --- a/drivers/clk/qcom/clk-krait.c +++ b/drivers/clk/qcom/clk-krait.c @@ -32,11 +32,16 @@ static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); } krait_set_l2_indirect_reg(mux->offset, regval); - spin_unlock_irqrestore(&krait_clock_reg_lock, flags); /* Wait for switch to complete. */ mb(); udelay(1); + + /* + * Unlock now to make sure the mux register is not + * modified while switching to the new parent. + */ + spin_unlock_irqrestore(&krait_clock_reg_lock, flags); } static int krait_mux_set_parent(struct clk_hw *hw, u8 index) @@ -93,6 +98,8 @@ static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, if (d->lpl) mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; + else + mask <<= d->shift; spin_lock_irqsave(&krait_clock_reg_lock, flags); val = krait_get_l2_indirect_reg(d->offset); diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 89c1adeb84d4..40374f3d3d20 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -146,17 +146,11 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) static unsigned long calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div) { - if (hid_div) { - rate *= 2; - rate /= hid_div + 1; - } + if (hid_div) + rate = mult_frac(rate, 2, hid_div + 1); - if (mode) { - u64 tmp = rate; - tmp *= m; - do_div(tmp, n); - rate = tmp; - } + if (mode) + rate = mult_frac(rate, m, n); return rate; } diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c index 0cc4909b5dbe..cb7a2d9247b0 100644 --- a/drivers/clk/qcom/dispcc-sdm845.c +++ b/drivers/clk/qcom/dispcc-sdm845.c @@ -569,6 +569,8 @@ static struct clk_branch disp_cc_mdss_vsync_clk = { static struct gdsc mdss_gdsc = { .gdscr = 0x3000, + .en_few_wait_val = 0x6, + .en_rest_wait_val = 0x5, .pd = { .name = "mdss_gdsc", }, diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c index de48ba7eba3a..b10ecfde21c8 100644 --- a/drivers/clk/qcom/gcc-ipq8074.c +++ b/drivers/clk/qcom/gcc-ipq8074.c @@ -423,7 +423,6 @@ static struct clk_fixed_factor gpll0_out_main_div2 = { }, .num_parents = 1, .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, }, }; @@ -470,7 +469,6 @@ static struct clk_alpha_pll_postdiv gpll2 = { }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, }, }; @@ -503,7 +501,6 @@ static struct clk_alpha_pll_postdiv gpll4 = { }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, }, }; @@ -537,7 +534,6 @@ static struct clk_alpha_pll_postdiv gpll6 = { }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, }, }; @@ -551,7 +547,6 @@ static struct clk_fixed_factor gpll6_out_main_div2 = { }, .num_parents = 1, .ops = &clk_fixed_factor_ops, - .flags = CLK_SET_RATE_PARENT, }, }; @@ -616,7 +611,6 @@ static struct clk_alpha_pll_postdiv nss_crypto_pll = { }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ro_ops, - .flags = CLK_SET_RATE_PARENT, }, }; @@ -667,6 +661,7 @@ static struct clk_branch gcc_sleep_clk_src = { }, .num_parents = 1, .ops = &clk_branch2_ops, + .flags = CLK_IS_CRITICAL, }, }, }; @@ -977,6 +972,7 @@ static struct clk_rcg2 pcie0_axi_clk_src = { static const struct freq_tbl ftbl_pcie_aux_clk_src[] = { F(19200000, P_XO, 1, 0, 0), + { } }; static struct clk_rcg2 pcie0_aux_clk_src = { @@ -1082,6 +1078,7 @@ static const struct freq_tbl ftbl_sdcc_ice_core_clk_src[] = { F(19200000, P_XO, 1, 0, 0), F(160000000, P_GPLL0, 5, 0, 0), F(308570000, P_GPLL6, 3.5, 0, 0), + { } }; static struct clk_rcg2 sdcc1_ice_core_clk_src = { @@ -1788,8 +1785,10 @@ static struct clk_regmap_div nss_port4_tx_div_clk_src = { static const struct freq_tbl ftbl_nss_port5_rx_clk_src[] = { F(19200000, P_XO, 1, 0, 0), F(25000000, P_UNIPHY1_RX, 12.5, 0, 0), + F(25000000, P_UNIPHY0_RX, 5, 0, 0), F(78125000, P_UNIPHY1_RX, 4, 0, 0), F(125000000, P_UNIPHY1_RX, 2.5, 0, 0), + F(125000000, P_UNIPHY0_RX, 1, 0, 0), F(156250000, P_UNIPHY1_RX, 2, 0, 0), F(312500000, P_UNIPHY1_RX, 1, 0, 0), { } @@ -1828,8 +1827,10 @@ static struct clk_regmap_div nss_port5_rx_div_clk_src = { static const struct freq_tbl ftbl_nss_port5_tx_clk_src[] = { F(19200000, P_XO, 1, 0, 0), F(25000000, P_UNIPHY1_TX, 12.5, 0, 0), + F(25000000, P_UNIPHY0_TX, 5, 0, 0), F(78125000, P_UNIPHY1_TX, 4, 0, 0), F(125000000, P_UNIPHY1_TX, 2.5, 0, 0), + F(125000000, P_UNIPHY0_TX, 1, 0, 0), F(156250000, P_UNIPHY1_TX, 2, 0, 0), F(312500000, P_UNIPHY1_TX, 1, 0, 0), { } @@ -1867,8 +1868,10 @@ static struct clk_regmap_div nss_port5_tx_div_clk_src = { static const struct freq_tbl ftbl_nss_port6_rx_clk_src[] = { F(19200000, P_XO, 1, 0, 0), + F(25000000, P_UNIPHY2_RX, 5, 0, 0), F(25000000, P_UNIPHY2_RX, 12.5, 0, 0), F(78125000, P_UNIPHY2_RX, 4, 0, 0), + F(125000000, P_UNIPHY2_RX, 1, 0, 0), F(125000000, P_UNIPHY2_RX, 2.5, 0, 0), F(156250000, P_UNIPHY2_RX, 2, 0, 0), F(312500000, P_UNIPHY2_RX, 1, 0, 0), @@ -1907,8 +1910,10 @@ static struct clk_regmap_div nss_port6_rx_div_clk_src = { static const struct freq_tbl ftbl_nss_port6_tx_clk_src[] = { F(19200000, P_XO, 1, 0, 0), + F(25000000, P_UNIPHY2_TX, 5, 0, 0), F(25000000, P_UNIPHY2_TX, 12.5, 0, 0), F(78125000, P_UNIPHY2_TX, 4, 0, 0), + F(125000000, P_UNIPHY2_TX, 1, 0, 0), F(125000000, P_UNIPHY2_TX, 2.5, 0, 0), F(156250000, P_UNIPHY2_TX, 2, 0, 0), F(312500000, P_UNIPHY2_TX, 1, 0, 0), @@ -3346,6 +3351,7 @@ static struct clk_branch gcc_nssnoc_ubi1_ahb_clk = { static struct clk_branch gcc_ubi0_ahb_clk = { .halt_reg = 0x6820c, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x6820c, .enable_mask = BIT(0), @@ -3363,6 +3369,7 @@ static struct clk_branch gcc_ubi0_ahb_clk = { static struct clk_branch gcc_ubi0_axi_clk = { .halt_reg = 0x68200, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68200, .enable_mask = BIT(0), @@ -3380,6 +3387,7 @@ static struct clk_branch gcc_ubi0_axi_clk = { static struct clk_branch gcc_ubi0_nc_axi_clk = { .halt_reg = 0x68204, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68204, .enable_mask = BIT(0), @@ -3397,6 +3405,7 @@ static struct clk_branch gcc_ubi0_nc_axi_clk = { static struct clk_branch gcc_ubi0_core_clk = { .halt_reg = 0x68210, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68210, .enable_mask = BIT(0), @@ -3414,6 +3423,7 @@ static struct clk_branch gcc_ubi0_core_clk = { static struct clk_branch gcc_ubi0_mpt_clk = { .halt_reg = 0x68208, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68208, .enable_mask = BIT(0), @@ -3431,6 +3441,7 @@ static struct clk_branch gcc_ubi0_mpt_clk = { static struct clk_branch gcc_ubi1_ahb_clk = { .halt_reg = 0x6822c, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x6822c, .enable_mask = BIT(0), @@ -3448,6 +3459,7 @@ static struct clk_branch gcc_ubi1_ahb_clk = { static struct clk_branch gcc_ubi1_axi_clk = { .halt_reg = 0x68220, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68220, .enable_mask = BIT(0), @@ -3465,6 +3477,7 @@ static struct clk_branch gcc_ubi1_axi_clk = { static struct clk_branch gcc_ubi1_nc_axi_clk = { .halt_reg = 0x68224, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68224, .enable_mask = BIT(0), @@ -3482,6 +3495,7 @@ static struct clk_branch gcc_ubi1_nc_axi_clk = { static struct clk_branch gcc_ubi1_core_clk = { .halt_reg = 0x68230, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68230, .enable_mask = BIT(0), @@ -3499,6 +3513,7 @@ static struct clk_branch gcc_ubi1_core_clk = { static struct clk_branch gcc_ubi1_mpt_clk = { .halt_reg = 0x68228, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68228, .enable_mask = BIT(0), diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c index 8bed02a748ab..470a277603a9 100644 --- a/drivers/clk/qcom/gcc-mdm9615.c +++ b/drivers/clk/qcom/gcc-mdm9615.c @@ -58,7 +58,7 @@ static struct clk_regmap pll0_vote = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "pll0_vote", - .parent_names = (const char *[]){ "pll8" }, + .parent_names = (const char *[]){ "pll0" }, .num_parents = 1, .ops = &clk_pll_vote_ops, }, diff --git a/drivers/clk/qcom/gcc-qcs404.c b/drivers/clk/qcom/gcc-qcs404.c index bd32212f37e6..5982253b0330 100644 --- a/drivers/clk/qcom/gcc-qcs404.c +++ b/drivers/clk/qcom/gcc-qcs404.c @@ -25,11 +25,9 @@ enum { P_CORE_BI_PLL_TEST_SE, P_DSI0_PHY_PLL_OUT_BYTECLK, P_DSI0_PHY_PLL_OUT_DSICLK, - P_GPLL0_OUT_AUX, P_GPLL0_OUT_MAIN, P_GPLL1_OUT_MAIN, P_GPLL3_OUT_MAIN, - P_GPLL4_OUT_AUX, P_GPLL4_OUT_MAIN, P_GPLL6_OUT_AUX, P_HDMI_PHY_PLL_CLK, @@ -109,28 +107,24 @@ static const char * const gcc_parent_names_4[] = { static const struct parent_map gcc_parent_map_5[] = { { P_XO, 0 }, { P_DSI0_PHY_PLL_OUT_BYTECLK, 1 }, - { P_GPLL0_OUT_AUX, 2 }, { P_CORE_BI_PLL_TEST_SE, 7 }, }; static const char * const gcc_parent_names_5[] = { "cxo", - "dsi0pll_byteclk_src", - "gpll0_out_aux", + "dsi0pllbyte", "core_bi_pll_test_se", }; static const struct parent_map gcc_parent_map_6[] = { { P_XO, 0 }, { P_DSI0_PHY_PLL_OUT_BYTECLK, 2 }, - { P_GPLL0_OUT_AUX, 3 }, { P_CORE_BI_PLL_TEST_SE, 7 }, }; static const char * const gcc_parent_names_6[] = { "cxo", - "dsi0_phy_pll_out_byteclk", - "gpll0_out_aux", + "dsi0pllbyte", "core_bi_pll_test_se", }; @@ -139,7 +133,6 @@ static const struct parent_map gcc_parent_map_7[] = { { P_GPLL0_OUT_MAIN, 1 }, { P_GPLL3_OUT_MAIN, 2 }, { P_GPLL6_OUT_AUX, 3 }, - { P_GPLL4_OUT_AUX, 4 }, { P_CORE_BI_PLL_TEST_SE, 7 }, }; @@ -148,7 +141,6 @@ static const char * const gcc_parent_names_7[] = { "gpll0_out_main", "gpll3_out_main", "gpll6_out_aux", - "gpll4_out_aux", "core_bi_pll_test_se", }; @@ -175,7 +167,7 @@ static const struct parent_map gcc_parent_map_9[] = { static const char * const gcc_parent_names_9[] = { "cxo", "gpll0_out_main", - "dsi0_phy_pll_out_dsiclk", + "dsi0pll", "gpll6_out_aux", "core_bi_pll_test_se", }; @@ -207,14 +199,12 @@ static const char * const gcc_parent_names_11[] = { static const struct parent_map gcc_parent_map_12[] = { { P_XO, 0 }, { P_DSI0_PHY_PLL_OUT_DSICLK, 1 }, - { P_GPLL0_OUT_AUX, 2 }, { P_CORE_BI_PLL_TEST_SE, 7 }, }; static const char * const gcc_parent_names_12[] = { "cxo", - "dsi0pll_pclk_src", - "gpll0_out_aux", + "dsi0pll", "core_bi_pll_test_se", }; @@ -237,40 +227,34 @@ static const char * const gcc_parent_names_13[] = { static const struct parent_map gcc_parent_map_14[] = { { P_XO, 0 }, { P_GPLL0_OUT_MAIN, 1 }, - { P_GPLL4_OUT_AUX, 2 }, { P_CORE_BI_PLL_TEST_SE, 7 }, }; static const char * const gcc_parent_names_14[] = { "cxo", "gpll0_out_main", - "gpll4_out_aux", "core_bi_pll_test_se", }; static const struct parent_map gcc_parent_map_15[] = { { P_XO, 0 }, - { P_GPLL0_OUT_AUX, 2 }, { P_CORE_BI_PLL_TEST_SE, 7 }, }; static const char * const gcc_parent_names_15[] = { "cxo", - "gpll0_out_aux", "core_bi_pll_test_se", }; static const struct parent_map gcc_parent_map_16[] = { { P_XO, 0 }, { P_GPLL0_OUT_MAIN, 1 }, - { P_GPLL0_OUT_AUX, 2 }, { P_CORE_BI_PLL_TEST_SE, 7 }, }; static const char * const gcc_parent_names_16[] = { "cxo", "gpll0_out_main", - "gpll0_out_aux", "core_bi_pll_test_se", }; diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index 56d22dd225c9..a8810a628e71 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -3646,3 +3646,4 @@ module_exit(gcc_sdm845_exit); MODULE_DESCRIPTION("QTI GCC SDM845 Driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:gcc-sdm845"); +MODULE_SOFTDEP("pre: rpmhpd"); diff --git a/drivers/clk/qcom/gcc-sm8150.c b/drivers/clk/qcom/gcc-sm8150.c index ee908fbfeab1..68ea6a846a15 100644 --- a/drivers/clk/qcom/gcc-sm8150.c +++ b/drivers/clk/qcom/gcc-sm8150.c @@ -250,7 +250,7 @@ static struct clk_rcg2 gcc_cpuss_ahb_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_cpuss_ahb_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -273,7 +273,7 @@ static struct clk_rcg2 gcc_emac_ptp_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_emac_ptp_clk_src", .parent_data = gcc_parents_5, - .num_parents = 5, + .num_parents = ARRAY_SIZE(gcc_parents_5), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -299,7 +299,7 @@ static struct clk_rcg2 gcc_emac_rgmii_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_emac_rgmii_clk_src", .parent_data = gcc_parents_5, - .num_parents = 5, + .num_parents = ARRAY_SIZE(gcc_parents_5), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -323,7 +323,7 @@ static struct clk_rcg2 gcc_gp1_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_gp1_clk_src", .parent_data = gcc_parents_1, - .num_parents = 5, + .num_parents = ARRAY_SIZE(gcc_parents_1), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -338,7 +338,7 @@ static struct clk_rcg2 gcc_gp2_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_gp2_clk_src", .parent_data = gcc_parents_1, - .num_parents = 5, + .num_parents = ARRAY_SIZE(gcc_parents_1), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -353,7 +353,7 @@ static struct clk_rcg2 gcc_gp3_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_gp3_clk_src", .parent_data = gcc_parents_1, - .num_parents = 5, + .num_parents = ARRAY_SIZE(gcc_parents_1), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -374,7 +374,7 @@ static struct clk_rcg2 gcc_pcie_0_aux_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_pcie_0_aux_clk_src", .parent_data = gcc_parents_2, - .num_parents = 3, + .num_parents = ARRAY_SIZE(gcc_parents_2), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -389,7 +389,7 @@ static struct clk_rcg2 gcc_pcie_1_aux_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_pcie_1_aux_clk_src", .parent_data = gcc_parents_2, - .num_parents = 3, + .num_parents = ARRAY_SIZE(gcc_parents_2), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -410,7 +410,7 @@ static struct clk_rcg2 gcc_pcie_phy_refgen_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_pcie_phy_refgen_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -432,7 +432,7 @@ static struct clk_rcg2 gcc_pdm2_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_pdm2_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -455,7 +455,7 @@ static struct clk_rcg2 gcc_qspi_core_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qspi_core_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -489,7 +489,7 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s0_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap0_s0_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -504,7 +504,7 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s1_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap0_s1_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -519,7 +519,7 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s2_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap0_s2_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -534,7 +534,7 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s3_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap0_s3_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -549,7 +549,7 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap0_s4_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -564,7 +564,7 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s5_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap0_s5_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -579,7 +579,7 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s6_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap0_s6_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -594,7 +594,7 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s7_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap0_s7_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -609,7 +609,7 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap1_s0_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -624,7 +624,7 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap1_s1_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -639,7 +639,7 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s2_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap1_s2_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -654,7 +654,7 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap1_s3_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -669,7 +669,7 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap1_s4_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -684,7 +684,7 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap1_s5_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -699,7 +699,7 @@ static struct clk_rcg2 gcc_qupv3_wrap2_s0_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap2_s0_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -714,7 +714,7 @@ static struct clk_rcg2 gcc_qupv3_wrap2_s1_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap2_s1_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -729,7 +729,7 @@ static struct clk_rcg2 gcc_qupv3_wrap2_s2_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap2_s2_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -744,7 +744,7 @@ static struct clk_rcg2 gcc_qupv3_wrap2_s3_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap2_s3_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -759,7 +759,7 @@ static struct clk_rcg2 gcc_qupv3_wrap2_s4_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap2_s4_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -774,7 +774,7 @@ static struct clk_rcg2 gcc_qupv3_wrap2_s5_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_qupv3_wrap2_s5_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -800,8 +800,8 @@ static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_sdcc2_apps_clk_src", .parent_data = gcc_parents_6, - .num_parents = 5, - .flags = CLK_SET_RATE_PARENT, + .num_parents = ARRAY_SIZE(gcc_parents_6), + .flags = CLK_OPS_PARENT_ENABLE, .ops = &clk_rcg2_floor_ops, }, }; @@ -825,7 +825,7 @@ static struct clk_rcg2 gcc_sdcc4_apps_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_sdcc4_apps_clk_src", .parent_data = gcc_parents_3, - .num_parents = 3, + .num_parents = ARRAY_SIZE(gcc_parents_3), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_floor_ops, }, @@ -845,7 +845,7 @@ static struct clk_rcg2 gcc_tsif_ref_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_tsif_ref_clk_src", .parent_data = gcc_parents_7, - .num_parents = 5, + .num_parents = ARRAY_SIZE(gcc_parents_7), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -869,7 +869,7 @@ static struct clk_rcg2 gcc_ufs_card_axi_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_axi_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -892,7 +892,7 @@ static struct clk_rcg2 gcc_ufs_card_ice_core_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_ice_core_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -912,7 +912,7 @@ static struct clk_rcg2 gcc_ufs_card_phy_aux_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_phy_aux_clk_src", .parent_data = gcc_parents_4, - .num_parents = 2, + .num_parents = ARRAY_SIZE(gcc_parents_4), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -934,7 +934,7 @@ static struct clk_rcg2 gcc_ufs_card_unipro_core_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_card_unipro_core_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -958,7 +958,7 @@ static struct clk_rcg2 gcc_ufs_phy_axi_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_axi_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -973,7 +973,7 @@ static struct clk_rcg2 gcc_ufs_phy_ice_core_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_ice_core_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -988,7 +988,7 @@ static struct clk_rcg2 gcc_ufs_phy_phy_aux_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_phy_aux_clk_src", .parent_data = gcc_parents_4, - .num_parents = 2, + .num_parents = ARRAY_SIZE(gcc_parents_4), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -1003,7 +1003,7 @@ static struct clk_rcg2 gcc_ufs_phy_unipro_core_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_ufs_phy_unipro_core_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -1027,7 +1027,7 @@ static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb30_prim_master_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -1049,7 +1049,7 @@ static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb30_prim_mock_utmi_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -1064,7 +1064,7 @@ static struct clk_rcg2 gcc_usb30_sec_master_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb30_sec_master_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -1079,7 +1079,7 @@ static struct clk_rcg2 gcc_usb30_sec_mock_utmi_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb30_sec_mock_utmi_clk_src", .parent_data = gcc_parents_0, - .num_parents = 4, + .num_parents = ARRAY_SIZE(gcc_parents_0), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -1094,7 +1094,7 @@ static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb3_prim_phy_aux_clk_src", .parent_data = gcc_parents_2, - .num_parents = 3, + .num_parents = ARRAY_SIZE(gcc_parents_2), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, @@ -1109,7 +1109,7 @@ static struct clk_rcg2 gcc_usb3_sec_phy_aux_clk_src = { .clkr.hw.init = &(struct clk_init_data){ .name = "gcc_usb3_sec_phy_aux_clk_src", .parent_data = gcc_parents_2, - .num_parents = 3, + .num_parents = ARRAY_SIZE(gcc_parents_2), .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c index e40efba1bf7d..7a116f62168b 100644 --- a/drivers/clk/qcom/gpucc-sdm845.c +++ b/drivers/clk/qcom/gpucc-sdm845.c @@ -22,8 +22,6 @@ #define CX_GMU_CBCR_SLEEP_SHIFT 4 #define CX_GMU_CBCR_WAKE_MASK 0xf #define CX_GMU_CBCR_WAKE_SHIFT 8 -#define CLK_DIS_WAIT_SHIFT 12 -#define CLK_DIS_WAIT_MASK (0xf << CLK_DIS_WAIT_SHIFT) enum { P_BI_TCXO, @@ -124,6 +122,7 @@ static struct clk_branch gpu_cc_cxo_clk = { static struct gdsc gpu_cx_gdsc = { .gdscr = 0x106c, .gds_hw_ctrl = 0x1540, + .clk_dis_wait_val = 0x8, .pd = { .name = "gpu_cx_gdsc", }, @@ -221,10 +220,6 @@ static int gpu_cc_sdm845_probe(struct platform_device *pdev) value = 0xf << CX_GMU_CBCR_WAKE_SHIFT | 0xf << CX_GMU_CBCR_SLEEP_SHIFT; regmap_update_bits(regmap, 0x1098, mask, value); - /* Configure clk_dis_wait for gpu_cx_gdsc */ - regmap_update_bits(regmap, 0x106c, CLK_DIS_WAIT_MASK, - 8 << CLK_DIS_WAIT_SHIFT); - return qcom_cc_really_probe(pdev, &gpu_cc_sdm845_desc, regmap); } diff --git a/drivers/clk/qcom/mmcc-apq8084.c b/drivers/clk/qcom/mmcc-apq8084.c index fbfcf0006739..c2fd0e8f4bc0 100644 --- a/drivers/clk/qcom/mmcc-apq8084.c +++ b/drivers/clk/qcom/mmcc-apq8084.c @@ -333,6 +333,7 @@ static struct freq_tbl ftbl_mmss_axi_clk[] = { F(333430000, P_MMPLL1, 3.5, 0, 0), F(400000000, P_MMPLL0, 2, 0, 0), F(466800000, P_MMPLL1, 2.5, 0, 0), + { } }; static struct clk_rcg2 mmss_axi_clk_src = { @@ -357,6 +358,7 @@ static struct freq_tbl ftbl_ocmemnoc_clk[] = { F(150000000, P_GPLL0, 4, 0, 0), F(228570000, P_MMPLL0, 3.5, 0, 0), F(320000000, P_MMPLL0, 2.5, 0, 0), + { } }; static struct clk_rcg2 ocmemnoc_clk_src = { diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index bcb0a397ef91..8ce1826aea14 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -283,6 +283,7 @@ static struct freq_tbl ftbl_mmss_axi_clk[] = { F(291750000, P_MMPLL1, 4, 0, 0), F(400000000, P_MMPLL0, 2, 0, 0), F(466800000, P_MMPLL1, 2.5, 0, 0), + { } }; static struct clk_rcg2 mmss_axi_clk_src = { @@ -307,6 +308,7 @@ static struct freq_tbl ftbl_ocmemnoc_clk[] = { F(150000000, P_GPLL0, 4, 0, 0), F(291750000, P_MMPLL1, 4, 0, 0), F(400000000, P_MMPLL0, 2, 0, 0), + { } }; static struct clk_rcg2 ocmemnoc_clk_src = { diff --git a/drivers/clk/qcom/reset.c b/drivers/clk/qcom/reset.c index 819d194be8f7..5a44dc8bd25f 100644 --- a/drivers/clk/qcom/reset.c +++ b/drivers/clk/qcom/reset.c @@ -13,14 +13,16 @@ static int qcom_reset(struct reset_controller_dev *rcdev, unsigned long id) { + struct qcom_reset_controller *rst = to_qcom_reset_controller(rcdev); + rcdev->ops->assert(rcdev, id); - udelay(1); + udelay(rst->reset_map[id].udelay ?: 1); /* use 1 us as default */ rcdev->ops->deassert(rcdev, id); return 0; } -static int -qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +static int qcom_reset_set_assert(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) { struct qcom_reset_controller *rst; const struct qcom_reset_map *map; @@ -28,23 +30,24 @@ qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) rst = to_qcom_reset_controller(rcdev); map = &rst->reset_map[id]; - mask = BIT(map->bit); + mask = map->bitmask ? map->bitmask : BIT(map->bit); + + regmap_update_bits(rst->regmap, map->reg, mask, assert ? mask : 0); + + /* Read back the register to ensure write completion, ignore the value */ + regmap_read(rst->regmap, map->reg, &mask); - return regmap_update_bits(rst->regmap, map->reg, mask, mask); + return 0; } -static int -qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +static int qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) { - struct qcom_reset_controller *rst; - const struct qcom_reset_map *map; - u32 mask; - - rst = to_qcom_reset_controller(rcdev); - map = &rst->reset_map[id]; - mask = BIT(map->bit); + return qcom_reset_set_assert(rcdev, id, true); +} - return regmap_update_bits(rst->regmap, map->reg, mask, 0); +static int qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + return qcom_reset_set_assert(rcdev, id, false); } const struct reset_control_ops qcom_reset_ops = { diff --git a/drivers/clk/qcom/reset.h b/drivers/clk/qcom/reset.h index 2a08b5e282c7..9a47c838d9b1 100644 --- a/drivers/clk/qcom/reset.h +++ b/drivers/clk/qcom/reset.h @@ -11,6 +11,8 @@ struct qcom_reset_map { unsigned int reg; u8 bit; + u8 udelay; + u32 bitmask; }; struct regmap; diff --git a/drivers/clk/renesas/r7s9210-cpg-mssr.c b/drivers/clk/renesas/r7s9210-cpg-mssr.c index cf65d4e0e116..2772eb0b8ba7 100644 --- a/drivers/clk/renesas/r7s9210-cpg-mssr.c +++ b/drivers/clk/renesas/r7s9210-cpg-mssr.c @@ -213,7 +213,7 @@ const struct cpg_mssr_info r7s9210_cpg_mssr_info __initconst = { .cpg_clk_register = rza2_cpg_clk_register, /* RZ/A2 has Standby Control Registers */ - .stbyctrl = true, + .reg_layout = CLK_REG_LAYOUT_RZ_A, }; static void __init r7s9210_cpg_mssr_early_init(struct device_node *np) diff --git a/drivers/clk/renesas/r9a06g032-clocks.c b/drivers/clk/renesas/r9a06g032-clocks.c index f2dc625b745d..75954ac1fb9b 100644 --- a/drivers/clk/renesas/r9a06g032-clocks.c +++ b/drivers/clk/renesas/r9a06g032-clocks.c @@ -286,8 +286,8 @@ static const struct r9a06g032_clkdesc r9a06g032_clocks[] = { .name = "uart_group_012", .type = K_BITSEL, .source = 1 + R9A06G032_DIV_UART, - /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG1_PR2 */ - .dual.sel = ((0xec / 4) << 5) | 24, + /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG0_0 */ + .dual.sel = ((0x34 / 4) << 5) | 30, .dual.group = 0, }, { @@ -295,8 +295,8 @@ static const struct r9a06g032_clkdesc r9a06g032_clocks[] = { .name = "uart_group_34567", .type = K_BITSEL, .source = 1 + R9A06G032_DIV_P2_PG, - /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG0_0 */ - .dual.sel = ((0x34 / 4) << 5) | 30, + /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG1_PR2 */ + .dual.sel = ((0xec / 4) << 5) | 24, .dual.group = 1, }, D_UGATE(CLK_UART0, "clk_uart0", UART_GROUP_012, 0, 0, 0x1b2, 0x1b3, 0x1b4, 0x1b5), @@ -386,7 +386,7 @@ static int r9a06g032_attach_dev(struct generic_pm_domain *pd, int error; int index; - while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, + while (!of_parse_phandle_with_args(np, "clocks", "#clock-cells", i++, &clkspec)) { if (clkspec.np != pd->dev.of_node) continue; @@ -399,7 +399,6 @@ static int r9a06g032_attach_dev(struct generic_pm_domain *pd, if (error) return error; } - i++; } return 0; diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 6f9612c169af..d0ccb52b0270 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -111,12 +111,12 @@ static const u16 srcr[] = { * @rcdev: Optional reset controller entity * @dev: CPG/MSSR device * @base: CPG/MSSR register block base address + * @reg_layout: CPG/MSSR register layout * @rmw_lock: protects RMW register accesses * @np: Device node in DT for this CPG/MSSR module * @num_core_clks: Number of Core Clocks in clks[] * @num_mod_clks: Number of Module Clocks in clks[] * @last_dt_core_clk: ID of the last Core Clock exported to DT - * @stbyctrl: This device has Standby Control Registers * @notifiers: Notifier chain to save/restore clock state for system resume * @smstpcr_saved[].mask: Mask of SMSTPCR[] bits under our control * @smstpcr_saved[].val: Saved values of SMSTPCR[] @@ -128,13 +128,13 @@ struct cpg_mssr_priv { #endif struct device *dev; void __iomem *base; + enum clk_reg_layout reg_layout; spinlock_t rmw_lock; struct device_node *np; unsigned int num_core_clks; unsigned int num_mod_clks; unsigned int last_dt_core_clk; - bool stbyctrl; struct raw_notifier_head notifiers; struct { @@ -177,7 +177,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) enable ? "ON" : "OFF"); spin_lock_irqsave(&priv->rmw_lock, flags); - if (priv->stbyctrl) { + if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) { value = readb(priv->base + STBCR(reg)); if (enable) value &= ~bitmask; @@ -199,7 +199,7 @@ static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable) spin_unlock_irqrestore(&priv->rmw_lock, flags); - if (!enable || priv->stbyctrl) + if (!enable || priv->reg_layout == CLK_REG_LAYOUT_RZ_A) return 0; for (i = 1000; i > 0; --i) { @@ -233,7 +233,7 @@ static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) struct cpg_mssr_priv *priv = clock->priv; u32 value; - if (priv->stbyctrl) + if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) value = readb(priv->base + STBCR(clock->index / 32)); else value = readl(priv->base + MSTPSR(clock->index / 32)); @@ -272,7 +272,7 @@ struct clk *cpg_mssr_clk_src_twocell_get(struct of_phandle_args *clkspec, case CPG_MOD: type = "module"; - if (priv->stbyctrl) { + if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) { idx = MOD_CLK_PACK_10(clkidx); range_check = 7 - (clkidx % 10); } else { @@ -800,7 +800,8 @@ static int cpg_mssr_suspend_noirq(struct device *dev) /* Save module registers with bits under our control */ for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) { if (priv->smstpcr_saved[reg].mask) - priv->smstpcr_saved[reg].val = priv->stbyctrl ? + priv->smstpcr_saved[reg].val = + priv->reg_layout == CLK_REG_LAYOUT_RZ_A ? readb(priv->base + STBCR(reg)) : readl(priv->base + SMSTPCR(reg)); } @@ -830,7 +831,7 @@ static int cpg_mssr_resume_noirq(struct device *dev) if (!mask) continue; - if (priv->stbyctrl) + if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) oldval = readb(priv->base + STBCR(reg)); else oldval = readl(priv->base + SMSTPCR(reg)); @@ -839,7 +840,7 @@ static int cpg_mssr_resume_noirq(struct device *dev) if (newval == oldval) continue; - if (priv->stbyctrl) { + if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) { writeb(newval, priv->base + STBCR(reg)); /* dummy read to ensure write has completed */ readb(priv->base + STBCR(reg)); @@ -861,8 +862,7 @@ static int cpg_mssr_resume_noirq(struct device *dev) } if (!i) - dev_warn(dev, "Failed to enable %s%u[0x%x]\n", - priv->stbyctrl ? "STB" : "SMSTP", reg, + dev_warn(dev, "Failed to enable SMSTP%u[0x%x]\n", reg, oldval & mask); } @@ -907,12 +907,11 @@ static int __init cpg_mssr_common_init(struct device *dev, goto out_err; } - cpg_mssr_priv = priv; priv->num_core_clks = info->num_total_core_clks; priv->num_mod_clks = info->num_hw_mod_clks; priv->last_dt_core_clk = info->last_dt_core_clk; RAW_INIT_NOTIFIER_HEAD(&priv->notifiers); - priv->stbyctrl = info->stbyctrl; + priv->reg_layout = info->reg_layout; for (i = 0; i < nclks; i++) priv->clks[i] = ERR_PTR(-ENOENT); @@ -921,6 +920,8 @@ static int __init cpg_mssr_common_init(struct device *dev, if (error) goto out_err; + cpg_mssr_priv = priv; + return 0; out_err: @@ -990,7 +991,7 @@ static int __init cpg_mssr_probe(struct platform_device *pdev) return error; /* Reset Controller not supported for Standby Control SoCs */ - if (info->stbyctrl) + if (priv->reg_layout == CLK_REG_LAYOUT_RZ_A) return 0; error = cpg_mssr_reset_controller_register(priv); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 4ddcdf3bfb95..f05521b5fa6e 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -85,6 +85,11 @@ struct mssr_mod_clk { struct device_node; +enum clk_reg_layout { + CLK_REG_LAYOUT_RCAR_GEN2_AND_GEN3 = 0, + CLK_REG_LAYOUT_RZ_A, +}; + /** * SoC-specific CPG/MSSR Description * @@ -105,6 +110,7 @@ struct device_node; * @crit_mod_clks: Array with Module Clock IDs of critical clocks that * should not be disabled without a knowledgeable driver * @num_crit_mod_clks: Number of entries in crit_mod_clks[] + * @reg_layout: CPG/MSSR register layout from enum clk_reg_layout * * @core_pm_clks: Array with IDs of Core Clocks that are suitable for Power * Management, in addition to Module Clocks @@ -112,10 +118,6 @@ struct device_node; * * @init: Optional callback to perform SoC-specific initialization * @cpg_clk_register: Optional callback to handle special Core Clock types - * - * @stbyctrl: This device has Standby Control Registers which are 8-bits - * wide, no status registers (MSTPSR) and have different address - * offsets. */ struct cpg_mssr_info { @@ -130,7 +132,7 @@ struct cpg_mssr_info { unsigned int num_core_clks; unsigned int last_dt_core_clk; unsigned int num_total_core_clks; - bool stbyctrl; + enum clk_reg_layout reg_layout; /* Module Clocks */ const struct mssr_mod_clk *mod_clks; diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 198417d56300..aa8a299ff704 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -963,6 +963,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, return mux_clk; err_pll: + kfree(pll->rate_table); clk_unregister(mux_clk); mux_clk = pll_clk; err_mux: diff --git a/drivers/clk/rockchip/clk-rk3128.c b/drivers/clk/rockchip/clk-rk3128.c index 4b1122e98e16..ddfe1c402e80 100644 --- a/drivers/clk/rockchip/clk-rk3128.c +++ b/drivers/clk/rockchip/clk-rk3128.c @@ -489,7 +489,7 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { GATE(HCLK_I2S_2CH, "hclk_i2s_2ch", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 2, GFLAGS), GATE(0, "hclk_usb_peri", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(9), 13, GFLAGS), GATE(HCLK_HOST2, "hclk_host2", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS), - GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 0, RK2928_CLKGATE_CON(3), 13, GFLAGS), + GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 13, GFLAGS), GATE(0, "hclk_peri_ahb", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(9), 14, GFLAGS), GATE(HCLK_SPDIF, "hclk_spdif", "hclk_peri", 0, RK2928_CLKGATE_CON(10), 9, GFLAGS), GATE(HCLK_TSP, "hclk_tsp", "hclk_peri", 0, RK2928_CLKGATE_CON(10), 12, GFLAGS), diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 77aebfb1d6d5..730020fcc7fe 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -751,6 +751,7 @@ static const char *const rk3188_critical_clocks[] __initconst = { "pclk_peri", "hclk_cpubus", "hclk_vio_bus", + "sclk_mac_lbtest", }; static struct rockchip_clk_provider *__init rk3188_common_clk_init(struct device_node *np) diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index ce1d2446f142..1a75cc9f68ef 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -1259,7 +1259,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { RK3399_CLKSEL_CON(56), 6, 2, MFLAGS, RK3399_CLKGATE_CON(10), 7, GFLAGS), - COMPOSITE_NOGATE(SCLK_CIF_OUT, "clk_cifout", mux_clk_cif_p, 0, + COMPOSITE_NOGATE(SCLK_CIF_OUT, "clk_cifout", mux_clk_cif_p, CLK_SET_RATE_PARENT, RK3399_CLKSEL_CON(56), 5, 1, MFLAGS, 0, 5, DFLAGS), /* gic */ diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index ac70ad785d8e..33df20f813d5 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -1390,6 +1390,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, if (ret) { pr_err("%s: failed to register pll clock %s : %d\n", __func__, pll_clk->name, ret); + kfree(pll->rate_table); kfree(pll); return; } diff --git a/drivers/clk/si5324.h b/drivers/clk/si5324.h new file mode 100644 index 000000000000..b3826e7b2f84 --- /dev/null +++ b/drivers/clk/si5324.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Si5324 clock generator platform data + * + * Copyright (C) 2017-2018 Xilinx, Inc. + */ + +#ifndef __LINUX_PLATFORM_DATA_SI5324_H__ +#define __LINUX_PLATFORM_DATA_SI5324_H__ + +/** + * enum si5324_pll_src - Si5324 pll clock source + * + * @SI5324_PLL_SRC_DEFAULT: Default, do not change eeprom config + * @SI5324_PLL_SRC_XTAL: Pll source clock is XTAL input + * @SI5324_PLL_SRC_CLKIN1: Pll source clock is CLKIN1 input + * @SI5324_PLL_SRC_CLKIN2: Pll source clock is CLKIN2 input + * + * Defines enums for clock sources. + */ +enum si5324_pll_src { + SI5324_PLL_SRC_XTAL = 0, + SI5324_PLL_SRC_CLKIN1 = 1, + SI5324_PLL_SRC_CLKIN2 = 2, +}; + +/** + * enum si5324_drive_strength - Si5324 clock output drive strength + * + * @SI5324_DRIVE_DEFAULT: Default, do not change eeprom config + * @SI5324_DRIVE_2MA: 2mA clock output drive strength + * @SI5324_DRIVE_4MA: 4mA clock output drive strength + * @SI5324_DRIVE_6MA: 6mA clock output drive strength + * @SI5324_DRIVE_8MA: 8mA clock output drive strength + * + * Defines enums for drive strength + */ +enum si5324_drive_strength { + SI5324_DRIVE_DEFAULT = 0, + SI5324_DRIVE_2MA = 2, + SI5324_DRIVE_4MA = 4, + SI5324_DRIVE_6MA = 6, + SI5324_DRIVE_8MA = 8, +}; + +/** + * struct si5324_clkout_config - Si5324 clock output configuration + * + * @drive: output drive strength + * @rate: clkout rate + */ +struct si5324_clkout_config { + enum si5324_drive_strength drive; + unsigned long rate; +}; + +/** + * struct si5324_platform_data - Platform data for the Si5324 clock driver + * + * @pll_src: Pll source clock setting + * @clkout: Array of clkout configuration + */ +struct si5324_platform_data { + enum si5324_pll_src pll_src; + struct si5324_clkout_config clkout[2]; +}; + +#endif diff --git a/drivers/clk/si5324drv.c b/drivers/clk/si5324drv.c new file mode 100644 index 000000000000..5c064a329e73 --- /dev/null +++ b/drivers/clk/si5324drv.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Si5324 clock driver + * + * Copyright (C) 2017-2018 Xilinx, Inc. + * + * Author: Venkateshwar Rao G <vgannava.xilinx.com> + * Leon Woestenberg <leon@sidebranch.com> + */ + +#include <linux/module.h> +#include <linux/types.h> +#include "si5324drv.h" + +/** + * si5324_rate_approx - Find closest rational approximation N2_LS/N3 fraction. + * + * @f: Holds the N2_LS/N3 fraction in 36.28 fixed point notation. + * @md: Holds the maximum denominator (N3) value allowed. + * @num: Store the numinator (N2_LS) found. + * @denom: Store the denominator (N3) found. + * + * This function finds the closest rational approximation. + * It allows only n/1 solution and as a part of the calculation + * multiply fraction until no digits after the decimal point and + * continued fraction and check denominator at each step. + */ +void si5324_rate_approx(u64 f, u64 md, u32 *num, u32 *denom) +{ + u64 a, h[3] = { 0, 1, 0 }, k[3] = { 1, 0, 0 }; + u64 x, d, m, n = 1; + int i = 0; + + if (md <= 1) { + *denom = 1; + *num = (u32)(f >> 28); + return; + } + + n <<= 28; + for (i = 0; i < 28; i++) { + if ((f & 0x1) == 0) { + n >>= 1; + f >>= 1; + } else { + break; + } + } + d = f; + + for (i = 0; i < 64; i++) { + a = n ? (div64_u64(d, n)) : 0; + if (i && !a) + break; + x = d; + d = n; + div64_u64_rem(x, n, &m); + n = m; + x = a; + if (k[1] * a + k[0] >= md) { + x = div64_u64((md - k[0]), k[1]); + if (x * 2 >= a || k[1] >= md) + i = 65; + else + break; + } + h[2] = x * h[1] + h[0]; + h[0] = h[1]; + h[1] = h[2]; + k[2] = x * k[1] + k[0]; + k[0] = k[1]; + k[1] = k[2]; + } + + *denom = (u32)k[1]; + *num = (u32)h[1]; +} + +/** + * si5324_find_n2ls - Search through the possible settings for the N2_LS. + * + * @settings: Holds the settings up till now. + * + * This function finds the best setting for N2_LS and N3n with the values + * for N1_HS, NCn_LS, and N2_HS. + * + * Return: 1 when the best possible result has been found, 0 on failure. + */ +static int si5324_find_n2ls(struct si5324_settingst *settings) +{ + u32 result = 0; + u64 f3_actual; + u64 fosc_actual; + u64 fout_actual; + u64 delta_fout; + u64 n2_ls_div_n3, mult_res; + u32 mult; + + n2_ls_div_n3 = div64_u64(div64_u64(div64_u64(settings->fosc, + (settings->fin >> SI5324_FIN_FOUT_SHIFT)), + (u64)settings->n2_hs), (u64)2); + + si5324_rate_approx(n2_ls_div_n3, settings->n31_max, &settings->n2_ls, + &settings->n31); + settings->n2_ls *= 2; + + if (settings->n2_ls < settings->n2_ls_min) { + mult = div64_u64(settings->n2_ls_min, settings->n2_ls); + div64_u64_rem(settings->n2_ls_min, settings->n2_ls, &mult_res); + mult = mult_res ? mult + 1 : mult; + settings->n2_ls *= mult; + settings->n31 *= mult; + } + + if (settings->n31 < settings->n31_min) { + mult = div64_u64(settings->n31_min, settings->n31); + div64_u64_rem(settings->n31_min, settings->n31, &mult_res); + mult = mult_res ? mult + 1 : mult; + settings->n2_ls *= mult; + settings->n31 *= mult; + } + pr_debug("Trying N2_LS = %d N3 = %d.\n", settings->n2_ls, + settings->n31); + + if (settings->n2_ls < settings->n2_ls_min || + settings->n2_ls > settings->n2_ls_max) { + pr_info("N2_LS out of range.\n"); + } else if ((settings->n31 < settings->n31_min) || + (settings->n31 > settings->n31_max)) { + pr_info("N3 out of range.\n"); + } else { + f3_actual = div64_u64(settings->fin, settings->n31); + fosc_actual = f3_actual * settings->n2_hs * settings->n2_ls; + fout_actual = div64_u64(fosc_actual, + (settings->n1_hs * settings->nc1_ls)); + delta_fout = fout_actual - settings->fout; + + if ((f3_actual < ((u64)SI5324_F3_MIN) << + SI5324_FIN_FOUT_SHIFT) || + (f3_actual > ((u64)SI5324_F3_MAX) << + SI5324_FIN_FOUT_SHIFT)) { + pr_debug("F3 frequency out of range.\n"); + } else if ((fosc_actual < ((u64)SI5324_FOSC_MIN) << + SI5324_FIN_FOUT_SHIFT) || + (fosc_actual > ((u64)SI5324_FOSC_MAX) << + SI5324_FIN_FOUT_SHIFT)) { + pr_debug("Fosc frequency out of range.\n"); + } else if ((fout_actual < ((u64)SI5324_FOUT_MIN) << + SI5324_FIN_FOUT_SHIFT) || + (fout_actual > ((u64)SI5324_FOUT_MAX) << + SI5324_FIN_FOUT_SHIFT)) { + pr_debug("Fout frequency out of range.\n"); + } else { + pr_debug("Found solution: fout = %dHz delta = %dHz.\n", + (u32)(fout_actual >> SI5324_FIN_FOUT_SHIFT), + (u32)(delta_fout >> SI5324_FIN_FOUT_SHIFT)); + pr_debug("fosc = %dkHz f3 = %dHz.\n", + (u32)((fosc_actual >> SI5324_FIN_FOUT_SHIFT) / + 1000), + (u32)(f3_actual >> SI5324_FIN_FOUT_SHIFT)); + + if (((u64)abs(delta_fout)) < + settings->best_delta_fout) { + settings->best_n1_hs = settings->n1_hs; + settings->best_nc1_ls = settings->nc1_ls; + settings->best_n2_hs = settings->n2_hs; + settings->best_n2_ls = settings->n2_ls; + settings->best_n3 = settings->n31; + settings->best_fout = fout_actual; + settings->best_delta_fout = abs(delta_fout); + if (delta_fout == 0) + result = 1; + } + } + } + return result; +} + +/** + * si5324_find_n2 - Find a valid setting for N2_HS and N2_LS. + * + * @settings: Holds the settings up till now. + * + * This function finds a valid settings for N2_HS and N2_LS. Iterates over + * all possibilities of N2_HS and then performs a binary search over the + * N2_LS values. + * + * Return: 1 when the best possible result has been found. + */ +static int si5324_find_n2(struct si5324_settingst *settings) +{ + u32 result = 0; + + for (settings->n2_hs = SI5324_N2_HS_MAX; settings->n2_hs >= + SI5324_N2_HS_MIN; settings->n2_hs--) { + pr_debug("Trying N2_HS = %d.\n", settings->n2_hs); + settings->n2_ls_min = (u32)(div64_u64(settings->fosc, + ((u64)(SI5324_F3_MAX * settings->n2_hs) + << SI5324_FIN_FOUT_SHIFT))); + + if (settings->n2_ls_min < SI5324_N2_LS_MIN) + settings->n2_ls_min = SI5324_N2_LS_MIN; + + settings->n2_ls_max = (u32)(div64_u64(settings->fosc, + ((u64)(SI5324_F3_MIN * + settings->n2_hs) << + SI5324_FIN_FOUT_SHIFT))); + if (settings->n2_ls_max > SI5324_N2_LS_MAX) + settings->n2_ls_max = SI5324_N2_LS_MAX; + + result = si5324_find_n2ls(settings); + if (result) + break; + } + return result; +} + +/** + * si5324_calc_ncls_limits - Calculates the valid range for NCn_LS. + * + * @settings: Holds the input and output frequencies and the setting + * for N1_HS. + * + * This function calculates the valid range for NCn_LS with the value + * for the output frequency and N1_HS already set in settings. + * + * Return: -1 when there are no valid settings, 0 otherwise. + */ +int si5324_calc_ncls_limits(struct si5324_settingst *settings) +{ + settings->nc1_ls_min = div64_u64(settings->n1_hs_min, + settings->n1_hs); + + if (settings->nc1_ls_min < SI5324_NC_LS_MIN) + settings->nc1_ls_min = SI5324_NC_LS_MIN; + if (settings->nc1_ls_min > 1 && (settings->nc1_ls_min & 0x1) == 1) + settings->nc1_ls_min++; + settings->nc1_ls_max = div64_u64(settings->n1_hs_max, settings->n1_hs); + + if (settings->nc1_ls_max > SI5324_NC_LS_MAX) + settings->nc1_ls_max = SI5324_NC_LS_MAX; + + if ((settings->nc1_ls_max & 0x1) == 1) + settings->nc1_ls_max--; + if ((settings->nc1_ls_max * settings->n1_hs < settings->n1_hs_min) || + (settings->nc1_ls_min * settings->n1_hs > settings->n1_hs_max)) + return -1; + + return 0; +} + +/** + * si5324_find_ncls - Find a valid setting for NCn_LS + * + * @settings: Holds the input and output frequencies, the setting for + * N1_HS, and the limits for NCn_LS. + * + * This function find a valid setting for NCn_LS that can deliver the correct + * output frequency. Assumes that the valid range is relatively small + * so a full search can be done (should be true for video clock frequencies). + * + * Return: 1 when the best possible result has been found. + */ +static int si5324_find_ncls(struct si5324_settingst *settings) +{ + u64 fosc_1; + u32 result; + + fosc_1 = settings->fout * settings->n1_hs; + for (settings->nc1_ls = settings->nc1_ls_min; + settings->nc1_ls <= settings->nc1_ls_max;) { + settings->fosc = fosc_1 * settings->nc1_ls; + pr_debug("Trying NCn_LS = %d: fosc = %dkHz.\n", + settings->nc1_ls, + (u32)(div64_u64((settings->fosc >> + SI5324_FIN_FOUT_SHIFT), 1000))); + + result = si5324_find_n2(settings); + if (result) + break; + if (settings->nc1_ls == 1) + settings->nc1_ls++; + else + settings->nc1_ls += 2; + } + return result; +} + +/** + * si5324_calcfreqsettings - Calculate the frequency settings + * + * @clkinfreq: Frequency of the input clock. + * @clkoutfreq: Desired output clock frequency. + * @clkactual: Actual clock frequency. + * @n1_hs: Set to the value for the N1_HS register. + * @ncn_ls: Set to the value for the NCn_LS register. + * @n2_hs: Set to the value for the N2_HS register. + * @n2_ls: Set to the value for the N2_LS register. + * @n3n: Set to the value for the N3n register. + * @bwsel: Set to the value for the BW_SEL register. + * + * This funciton calculates the frequency settings for the desired output + * frequency. + * + * Return: SI5324_SUCCESS for success, SI5324_ERR_FREQ when the + * requested frequency cannot be generated. + */ +int si5324_calcfreqsettings(u32 clkinfreq, u32 clkoutfreq, u32 *clkactual, + u8 *n1_hs, u32 *ncn_ls, u8 *n2_hs, u32 *n2_ls, + u32 *n3n, u8 *bwsel) +{ + struct si5324_settingst settings; + int result; + + settings.fin = (u64)clkinfreq << SI5324_FIN_FOUT_SHIFT; + settings.fout = (u64)clkoutfreq << SI5324_FIN_FOUT_SHIFT; + settings.best_delta_fout = settings.fout; + + settings.n1_hs_min = (int)(div64_u64(SI5324_FOSC_MIN, clkoutfreq)); + if (settings.n1_hs_min < SI5324_N1_HS_MIN * SI5324_NC_LS_MIN) + settings.n1_hs_min = SI5324_N1_HS_MIN * SI5324_NC_LS_MIN; + + settings.n1_hs_max = (int)(div64_u64(SI5324_FOSC_MAX, clkoutfreq)); + if (settings.n1_hs_max > SI5324_N1_HS_MAX * SI5324_NC_LS_MAX) + settings.n1_hs_max = SI5324_N1_HS_MAX * SI5324_NC_LS_MAX; + + settings.n31_min = div64_u64(clkinfreq, SI5324_F3_MAX); + if (settings.n31_min < SI5324_N3_MIN) + settings.n31_min = SI5324_N3_MIN; + + settings.n31_max = div64_u64(clkinfreq, SI5324_F3_MIN); + if (settings.n31_max > SI5324_N3_MAX) + settings.n31_max = SI5324_N3_MAX; + + /* Find a valid oscillator frequency with the highest setting of N1_HS + * possible (reduces power) + */ + for (settings.n1_hs = SI5324_N1_HS_MAX; + settings.n1_hs >= SI5324_N1_HS_MIN; settings.n1_hs--) { + pr_debug("Trying N1_HS = %d.\n", settings.n1_hs); + + result = si5324_calc_ncls_limits(&settings); + if (result) { + pr_debug("No valid settings\n"); + continue; + } + result = si5324_find_ncls(&settings); + if (result) + break; + } + + pr_debug("Si5324: settings.best_delta_fout = %llu\n", + (unsigned long long)settings.best_delta_fout); + pr_debug("Si5324: settings.fout = %llu\n", + (unsigned long long)settings.fout); + + if (settings.best_delta_fout == settings.fout) { + pr_debug("Si5324: No valid settings found."); + return SI5324_ERR_FREQ; + } + pr_debug("Si5324: Found solution: fout = %dHz.\n", + (u32)(settings.best_fout >> 28)); + + /* Post processing: convert temporary values to actual registers */ + *n1_hs = (u8)settings.best_n1_hs - 4; + *ncn_ls = settings.best_nc1_ls - 1; + *n2_hs = (u8)settings.best_n2_hs - 4; + *n2_ls = settings.best_n2_ls - 1; + *n3n = settings.best_n3 - 1; + /* + * How must the bandwidth selection be determined? + * Not all settings will be valid. + * refclk 2, 0xA2, BWSEL_REG=1010 (?) + * free running 2, 0x42, BWSEL_REG=0100 (?) + */ + *bwsel = 6; + + if (clkactual) + *clkactual = (settings.best_fout >> SI5324_FIN_FOUT_SHIFT); + + return SI5324_SUCCESS; +} diff --git a/drivers/clk/si5324drv.h b/drivers/clk/si5324drv.h new file mode 100644 index 000000000000..28ea3050d5fb --- /dev/null +++ b/drivers/clk/si5324drv.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Si5324 clock driver + * + * Copyright (C) 2017-2018 Xilinx, Inc. + */ + +#ifndef SI5324DRV_H_ +#define SI5324DRV_H_ + +#include <linux/types.h> + +/****************************************************************************** + * User settable defines that depend on the specific board design. + * The defaults are for the Xilinx KC705 board. + *****************************************************************************/ + +#define SI5324_XTAL_FREQ 114285000UL + +/****************************************************************************** + * Defines independent on the specific board design. Should not be changed. + *****************************************************************************/ + +#define SI5324_SUCCESS 0 /*< Operation was successful */ +#define SI5324_ERR_IIC -1 /*< IIC error occurred */ +#define SI5324_ERR_FREQ -2 /*< Could not calculate frequency setting */ +#define SI5324_ERR_PARM -3 /*< Invalid parameter */ + +#define SI5324_CLKSRC_CLK1 1 /*< Use clock input 1 */ +#define SI5324_CLKSRC_CLK2 2 /*< Use clock input 2 */ +#define SI5324_CLKSRC_XTAL 3 /*< Use crystal (free running mode) */ + +#define SI5324_FOSC_MIN 4850000000UL /*< Min oscillator frequency */ +#define SI5324_FOSC_MAX 5670000000UL /*< Max oscillator frequency */ +#define SI5324_F3_MIN 10000 /*< Min phase detector frequency */ +#define SI5324_F3_MAX 2000000 /*< Max phase detector frequency */ +#define SI5324_FIN_MIN 2000 /*< Min input frequency */ +#define SI5324_FIN_MAX 710000000UL /*< Max input frequency */ +#define SI5324_FOUT_MIN 2000 /*< Min output frequency */ +#define SI5324_FOUT_MAX 945000000UL /*< Max output frequency */ + +#define SI5324_N1_HS_MIN 6 +#define SI5324_N1_HS_MAX 11 +#define SI5324_NC_LS_MIN 1 +#define SI5324_NC_LS_MAX 0x100000 +#define SI5324_N2_HS_MIN 4 +#define SI5324_N2_HS_MAX 11 +#define SI5324_N2_LS_MIN 2 /* even values only */ +#define SI5324_N2_LS_MAX 0x100000 +#define SI5324_N3_MIN 1 +#define SI5324_N3_MAX 0x080000 +#define SI5324_FIN_FOUT_SHIFT 28 + +struct si5324_settingst { + /* high-speed output divider */ + u32 n1_hs_min; + u32 n1_hs_max; + u32 n1_hs; + + /* low-speed output divider for clkout1 */ + u32 nc1_ls_min; + u32 nc1_ls_max; + u32 nc1_ls; + + /* low-speed output divider for clkout2 */ + u32 nc2_ls_min; + u32 nc2_ls_max; + u32 nc2_ls; + + /* high-speed feedback divider (PLL multiplier) */ + u32 n2_hs; + /* low-speed feedback divider (PLL multiplier) */ + u32 n2_ls_min; + u32 n2_ls_max; + u32 n2_ls; + + /* input divider for clk1 */ + u32 n31_min; + u32 n31_max; + u32 n31; + + u64 fin; + u64 fout; + u64 fosc; + u64 best_delta_fout; + u64 best_fout; + u32 best_n1_hs; + u32 best_nc1_ls; + u32 best_n2_hs; + u32 best_n2_ls; + u32 best_n3; +}; + +int si5324_calcfreqsettings(u32 clkinfreq, u32 clkoutfreq, u32 *clkactual, + u8 *n1_hs, u32 *ncn_ls, u8 *n2_hs, + u32 *n2_ls, u32 *n3n, u8 *bwsel); +void si5324_rate_approx(u64 f, u64 md, u32 *num, u32 *denom); +int si5324_calc_ncls_limits(struct si5324_settingst *settings); + +#endif /* SI5324DRV_H_ */ diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index cf94a12459ea..ee2a2d284113 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -174,21 +174,24 @@ void __init socfpga_gate_init(struct device_node *node) u32 div_reg[3]; u32 clk_phase[2]; u32 fixed_div; - struct clk *clk; + struct clk_hw *hw_clk; struct socfpga_gate_clk *socfpga_clk; const char *clk_name = node->name; const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; struct clk_ops *ops; int rc; + int err; socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); if (WARN_ON(!socfpga_clk)) return; ops = kmemdup(&gateclk_ops, sizeof(gateclk_ops), GFP_KERNEL); - if (WARN_ON(!ops)) + if (WARN_ON(!ops)) { + kfree(socfpga_clk); return; + } rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); if (rc) @@ -238,12 +241,15 @@ void __init socfpga_gate_init(struct device_node *node) init.parent_names = parent_name; socfpga_clk->hw.hw.init = &init; - clk = clk_register(NULL, &socfpga_clk->hw.hw); - if (WARN_ON(IS_ERR(clk))) { + hw_clk = &socfpga_clk->hw.hw; + + err = clk_hw_register(NULL, hw_clk); + if (err) { + kfree(ops); kfree(socfpga_clk); return; } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + rc = of_clk_add_provider(node, of_clk_src_simple_get, hw_clk); if (WARN_ON(rc)) return; } diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c index 5e0c4b45f77f..43707e2d7248 100644 --- a/drivers/clk/socfpga/clk-periph.c +++ b/drivers/clk/socfpga/clk-periph.c @@ -51,7 +51,7 @@ static __init void __socfpga_periph_init(struct device_node *node, const struct clk_ops *ops) { u32 reg; - struct clk *clk; + struct clk_hw *hw_clk; struct socfpga_periph_clk *periph_clk; const char *clk_name = node->name; const char *parent_name[SOCFPGA_MAX_PARENTS]; @@ -94,13 +94,13 @@ static __init void __socfpga_periph_init(struct device_node *node, init.parent_names = parent_name; periph_clk->hw.hw.init = &init; + hw_clk = &periph_clk->hw.hw; - clk = clk_register(NULL, &periph_clk->hw.hw); - if (WARN_ON(IS_ERR(clk))) { + if (clk_hw_register(NULL, hw_clk)) { kfree(periph_clk); return; } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + rc = of_clk_add_provider(node, of_clk_src_simple_get, hw_clk); } void __init socfpga_periph_init(struct device_node *node) diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index dc65cc0fd3bd..004e196492c4 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -70,17 +70,18 @@ static struct clk_ops clk_pll_ops = { .get_parent = clk_pll_get_parent, }; -static __init struct clk *__socfpga_pll_init(struct device_node *node, +static __init struct clk_hw *__socfpga_pll_init(struct device_node *node, const struct clk_ops *ops) { u32 reg; - struct clk *clk; + struct clk_hw *hw_clk; struct socfpga_pll *pll_clk; const char *clk_name = node->name; const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; struct device_node *clkmgr_np; int rc; + int err; of_property_read_u32(node, "reg", ®); @@ -108,13 +109,15 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, clk_pll_ops.enable = clk_gate_ops.enable; clk_pll_ops.disable = clk_gate_ops.disable; - clk = clk_register(NULL, &pll_clk->hw.hw); - if (WARN_ON(IS_ERR(clk))) { + hw_clk = &pll_clk->hw.hw; + + err = clk_hw_register(NULL, hw_clk); + if (err) { kfree(pll_clk); - return NULL; + return ERR_PTR(err); } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - return clk; + rc = of_clk_add_provider(node, of_clk_src_simple_get, hw_clk); + return hw_clk; } void __init socfpga_pll_init(struct device_node *node) diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index a156bd0c6af7..9eff05386ef9 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -943,9 +943,10 @@ static void __init st_of_quadfs_setup(struct device_node *np, clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name, data, reg, lock); - if (IS_ERR(clk)) + if (IS_ERR(clk)) { + kfree(lock); goto err_exit; - else + } else pr_debug("%s: parent %s rate %u\n", __clk_get_name(clk), __clk_get_name(clk_get_parent(clk)), diff --git a/drivers/clk/sunxi-ng/ccu_mmc_timing.c b/drivers/clk/sunxi-ng/ccu_mmc_timing.c index de33414fc5c2..c6a6ce98ca03 100644 --- a/drivers/clk/sunxi-ng/ccu_mmc_timing.c +++ b/drivers/clk/sunxi-ng/ccu_mmc_timing.c @@ -43,7 +43,7 @@ int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode) EXPORT_SYMBOL_GPL(sunxi_ccu_set_mmc_timing_mode); /** - * sunxi_ccu_set_mmc_timing_mode: Get the current MMC clock timing mode + * sunxi_ccu_get_mmc_timing_mode: Get the current MMC clock timing mode * @clk: clock to query * * Returns 0 if the clock is in old timing mode, > 0 if it is in diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c index a66263b6490d..00845044c98e 100644 --- a/drivers/clk/tegra/clk-bpmp.c +++ b/drivers/clk/tegra/clk-bpmp.c @@ -159,7 +159,7 @@ static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw, err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); if (err < 0) - return err; + return 0; return response.rate; } diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 0c1b83bedb73..eb2411a4cd78 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -459,6 +459,7 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, err = load_one_timing_from_dt(tegra, timing, child); if (err) { of_node_put(child); + kfree(tegra->timings); return err; } @@ -510,6 +511,7 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, err = load_timings_from_dt(tegra, node, node_ram_code); if (err) { of_node_put(node); + kfree(tegra); return ERR_PTR(err); } } diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 4efcaaf51b3a..586b091651dd 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -1337,6 +1337,7 @@ static void __init tegra114_clock_init(struct device_node *np) } pmc_base = of_iomap(node, 0); + of_node_put(node); if (!pmc_base) { pr_err("Can't map pmc registers\n"); WARN_ON(1); diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index bcd871134f45..3f74497d73e5 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -18,24 +18,24 @@ #define MISC_CLK_ENB 0x48 #define OSC_CTRL 0x50 -#define OSC_CTRL_OSC_FREQ_MASK (3<<30) -#define OSC_CTRL_OSC_FREQ_13MHZ (0<<30) -#define OSC_CTRL_OSC_FREQ_19_2MHZ (1<<30) -#define OSC_CTRL_OSC_FREQ_12MHZ (2<<30) -#define OSC_CTRL_OSC_FREQ_26MHZ (3<<30) -#define OSC_CTRL_MASK (0x3f2 | OSC_CTRL_OSC_FREQ_MASK) - -#define OSC_CTRL_PLL_REF_DIV_MASK (3<<28) -#define OSC_CTRL_PLL_REF_DIV_1 (0<<28) -#define OSC_CTRL_PLL_REF_DIV_2 (1<<28) -#define OSC_CTRL_PLL_REF_DIV_4 (2<<28) +#define OSC_CTRL_OSC_FREQ_MASK (3u<<30) +#define OSC_CTRL_OSC_FREQ_13MHZ (0u<<30) +#define OSC_CTRL_OSC_FREQ_19_2MHZ (1u<<30) +#define OSC_CTRL_OSC_FREQ_12MHZ (2u<<30) +#define OSC_CTRL_OSC_FREQ_26MHZ (3u<<30) +#define OSC_CTRL_MASK (0x3f2u | OSC_CTRL_OSC_FREQ_MASK) + +#define OSC_CTRL_PLL_REF_DIV_MASK (3u<<28) +#define OSC_CTRL_PLL_REF_DIV_1 (0u<<28) +#define OSC_CTRL_PLL_REF_DIV_2 (1u<<28) +#define OSC_CTRL_PLL_REF_DIV_4 (2u<<28) #define OSC_FREQ_DET 0x58 -#define OSC_FREQ_DET_TRIG (1<<31) +#define OSC_FREQ_DET_TRIG (1u<<31) #define OSC_FREQ_DET_STATUS 0x5c -#define OSC_FREQ_DET_BUSY (1<<31) -#define OSC_FREQ_DET_CNT_MASK 0xFFFF +#define OSC_FREQ_DET_BUSYu (1<<31) +#define OSC_FREQ_DET_CNT_MASK 0xFFFFu #define TEGRA20_CLK_PERIPH_BANKS 3 @@ -1151,6 +1151,7 @@ static void __init tegra20_clock_init(struct device_node *np) } pmc_base = of_iomap(node, 0); + of_node_put(node); if (!pmc_base) { pr_err("Can't map pmc registers\n"); BUG(); diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index df172d5772d7..34155b5b994d 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -3523,6 +3523,7 @@ static void __init tegra210_clock_init(struct device_node *np) } pmc_base = of_iomap(node, 0); + of_node_put(node); if (!pmc_base) { pr_err("Can't map pmc registers\n"); WARN_ON(1); diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index f65e16c4f3c4..62ea790d79f9 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -252,14 +252,16 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) if (rc) { pr_err("%s: failed to lookup atl clock %d\n", __func__, i); - return -EINVAL; + ret = -EINVAL; + goto pm_put; } clk = of_clk_get_from_provider(&clkspec); if (IS_ERR(clk)) { pr_err("%s: failed to get atl clock %d from provider\n", __func__, i); - return PTR_ERR(clk); + ret = PTR_ERR(clk); + goto pm_put; } cdesc = to_atl_desc(__clk_get_hw(clk)); @@ -292,8 +294,9 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) if (cdesc->enabled) atl_clk_enable(__clk_get_hw(clk)); } - pm_runtime_put_sync(cinfo->dev); +pm_put: + pm_runtime_put_sync(cinfo->dev); return ret; } diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index ffbb9008c1c9..f7cde869c7f7 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -37,6 +37,7 @@ static void __iomem *zynq_clkc_base; #define SLCR_CAN_MIOCLK_CTRL (zynq_clkc_base + 0x60) #define SLCR_DBG_CLK_CTRL (zynq_clkc_base + 0x64) #define SLCR_PCAP_CLK_CTRL (zynq_clkc_base + 0x68) +#define SLCR_TOPSW_CLK_CTRL (zynq_clkc_base + 0x6c) #define SLCR_FPGA0_CLK_CTRL (zynq_clkc_base + 0x70) #define SLCR_621_TRUE (zynq_clkc_base + 0xc4) #define SLCR_SWDT_CLK_SEL (zynq_clkc_base + 0x204) @@ -99,6 +100,48 @@ static const char *const gem1_emio_input_names[] __initconst = { static const char *const swdt_ext_clk_input_names[] __initconst = { "swdt_ext_clk"}; +#ifdef CONFIG_SUSPEND +static struct clk *iopll_save_parent; + +#define TOPSW_CLK_CTRL_DIS_MASK BIT(0) + +int zynq_clk_suspend_early(void) +{ + int ret; + + iopll_save_parent = clk_get_parent(clks[iopll]); + + ret = clk_set_parent(clks[iopll], ps_clk); + if (ret) + pr_info("%s: reparent iopll failed %d\n", __func__, ret); + + return 0; +} + +void zynq_clk_resume_late(void) +{ + clk_set_parent(clks[iopll], iopll_save_parent); +} + +void zynq_clk_topswitch_enable(void) +{ + u32 reg; + + reg = readl(SLCR_TOPSW_CLK_CTRL); + reg &= ~TOPSW_CLK_CTRL_DIS_MASK; + writel(reg, SLCR_TOPSW_CLK_CTRL); +} + +void zynq_clk_topswitch_disable(void) +{ + u32 reg; + + reg = readl(SLCR_TOPSW_CLK_CTRL); + reg |= TOPSW_CLK_CTRL_DIS_MASK; + writel(reg, SLCR_TOPSW_CLK_CTRL); +} +#endif + static void __init zynq_clk_register_fclk(enum zynq_clk fclk, const char *clk_name, void __iomem *fclk_ctrl_reg, const char **parents, int enable) diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c index 0af8f74c5fa5..880ea34c0038 100644 --- a/drivers/clk/zynqmp/clk-mux-zynqmp.c +++ b/drivers/clk/zynqmp/clk-mux-zynqmp.c @@ -85,7 +85,7 @@ static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index) static const struct clk_ops zynqmp_clk_mux_ops = { .get_parent = zynqmp_clk_mux_get_parent, .set_parent = zynqmp_clk_mux_set_parent, - .determine_rate = __clk_mux_determine_rate, + .determine_rate = __clk_mux_determine_rate_closest, }; static const struct clk_ops zynqmp_clk_mux_ro_ops = { diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c index 6f057ab9df03..06cd4401e4f1 100644 --- a/drivers/clk/zynqmp/clkc.c +++ b/drivers/clk/zynqmp/clkc.c @@ -34,6 +34,8 @@ #define END_OF_PARENTS 1 #define RESERVED_CLK_NAME "" +#define CLK_TYPE_FLAG2_FIELD_MASK GENMASK(7, 4) +#define CLK_TYPE_FLAG_BITS 8 #define CLK_GET_NAME_RESP_LEN 16 #define CLK_GET_TOPOLOGY_RESP_WORDS 3 #define CLK_GET_PARENTS_RESP_WORDS 3 @@ -396,6 +398,9 @@ static int __zynqmp_clock_get_topology(struct clock_topology *topology, topology[*nnodes].type_flag = FIELD_GET(CLK_TOPOLOGY_TYPE_FLAGS, response->topology[i]); + topology[*nnodes].type_flag |= + FIELD_GET(CLK_TYPE_FLAG2_FIELD_MASK, response->topology[i]) << + CLK_TYPE_FLAG_BITS; (*nnodes)++; } @@ -679,6 +684,13 @@ static void zynqmp_get_clock_info(void) FIELD_PREP(CLK_ATTR_NODE_INDEX, i); zynqmp_pm_clock_get_name(clock[i].clk_id, &name); + + /* + * Terminate with NULL character in case name provided by firmware + * is longer and truncated due to size limit. + */ + name.name[sizeof(name.name) - 1] = '\0'; + if (!strcmp(name.name, RESERVED_CLK_NAME)) continue; strncpy(clock[i].clk_name, name.name, MAX_NAME_LEN); @@ -752,6 +764,7 @@ static int zynqmp_clock_probe(struct platform_device *pdev) static const struct of_device_id zynqmp_clock_of_match[] = { {.compatible = "xlnx,zynqmp-clk"}, + {.compatible = "xlnx,versal-clk"}, {}, }; MODULE_DEVICE_TABLE(of, zynqmp_clock_of_match); diff --git a/drivers/clk/zynqmp/divider.c b/drivers/clk/zynqmp/divider.c index d8f5b70d2709..cb118775778a 100644 --- a/drivers/clk/zynqmp/divider.c +++ b/drivers/clk/zynqmp/divider.c @@ -25,7 +25,7 @@ #define to_zynqmp_clk_divider(_hw) \ container_of(_hw, struct zynqmp_clk_divider, hw) -#define CLK_FRAC BIT(13) /* has a fractional parent */ +#define CLK_FRAC BIT(8) /* has a fractional parent */ /** * struct zynqmp_clk_divider - adjustable divider clock @@ -34,19 +34,38 @@ * @is_frac: The divider is a fractional divider * @clk_id: Id of clock * @div_type: divisor type (TYPE_DIV1 or TYPE_DIV2) + * @max_div: Maximum supported divisor */ struct zynqmp_clk_divider { struct clk_hw hw; - u8 flags; + u16 flags; bool is_frac; u32 clk_id; u32 div_type; + u32 max_div; }; static inline int zynqmp_divider_get_val(unsigned long parent_rate, - unsigned long rate) + unsigned long rate, u16 flags) { - return DIV_ROUND_CLOSEST(parent_rate, rate); + int up, down; + unsigned long up_rate, down_rate; + + if (flags & CLK_DIVIDER_POWER_OF_TWO) { + up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + down = parent_rate / rate; + + up = __roundup_pow_of_two(up); + down = __rounddown_pow_of_two(down); + + up_rate = DIV_ROUND_UP_ULL((u64)parent_rate, up); + down_rate = DIV_ROUND_UP_ULL((u64)parent_rate, down); + + return (rate - up_rate) <= (down_rate - rate) ? up : down; + + } else { + return DIV_ROUND_CLOSEST(parent_rate, rate); + } } /** @@ -78,6 +97,9 @@ static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw, else value = div >> 16; + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + value = 1 << value; + if (!value) { WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", @@ -88,6 +110,34 @@ static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw, return DIV_ROUND_UP_ULL(parent_rate, value); } +static void zynqmp_compute_divider(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u32 max_div, + int *bestdiv) +{ + int div1; + int div2; + long error = LONG_MAX; + struct clk_hw *parent_hw = clk_hw_get_parent(hw); + struct zynqmp_clk_divider *pdivider = to_zynqmp_clk_divider(parent_hw); + + if (!pdivider) + return; + + *bestdiv = 1; + for (div1 = 1; div1 <= pdivider->max_div; div1++) { + for (div2 = 1; div2 <= max_div; div2++) { + long new_error = ((parent_rate / div1) / div2) - rate; + + if (abs(new_error) < abs(error)) { + *bestdiv = div2; + error = new_error; + } + } + } +} + /** * zynqmp_clk_divider_round_rate() - Round rate of divider clock * @hw: handle between common and hardware-specific interfaces @@ -120,13 +170,29 @@ static long zynqmp_clk_divider_round_rate(struct clk_hw *hw, else bestdiv = bestdiv >> 16; + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + bestdiv = 1 << bestdiv; + return DIV_ROUND_UP_ULL((u64)*prate, bestdiv); } - bestdiv = zynqmp_divider_get_val(*prate, rate); + bestdiv = zynqmp_divider_get_val(*prate, rate, divider->flags); + + /* + * In case of two divisors, compute best divider values and return + * divider2 value based on compute value. div1 will be automatically + * set to optimum based on required total divider value. + */ + if (div_type == TYPE_DIV2 && + (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { + zynqmp_compute_divider(hw, rate, *prate, + divider->max_div, &bestdiv); + } if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) && divider->is_frac) bestdiv = rate % *prate ? 1 : bestdiv; + + bestdiv = min_t(u32, bestdiv, divider->max_div); *prate = rate * bestdiv; return rate; @@ -151,7 +217,7 @@ static int zynqmp_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, int ret; const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - value = zynqmp_divider_get_val(parent_rate, rate); + value = zynqmp_divider_get_val(parent_rate, rate, divider->flags); if (div_type == TYPE_DIV1) { div = value & 0xFFFF; div |= 0xffff << 16; @@ -160,6 +226,9 @@ static int zynqmp_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, div |= value << 16; } + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + div = __ffs(div); + ret = eemi_ops->clock_setdivider(clk_id, div); if (ret) @@ -195,6 +264,9 @@ struct clk_hw *zynqmp_clk_register_divider(const char *name, struct clk_hw *hw; struct clk_init_data init; int ret; + const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); + struct zynqmp_pm_query_data qdata = {0}; + u32 ret_payload[PAYLOAD_ARG_CNT]; /* allocate the divider */ div = kzalloc(sizeof(*div), GFP_KERNEL); @@ -215,6 +287,21 @@ struct clk_hw *zynqmp_clk_register_divider(const char *name, div->clk_id = clk_id; div->div_type = nodes->type; + /* + * To achieve best possible rate, maximum limit of divider is required + * while computation. Get maximum supported divisor from firmware. To + * maintain backward compatibility assign maximum possible value(0xFFFF) + * if query for max divisor is not successful. + */ + qdata.qid = PM_QID_CLOCK_GET_MAX_DIVISOR; + qdata.arg1 = clk_id; + qdata.arg2 = nodes->type; + ret = eemi_ops->query_data(qdata, ret_payload); + if (ret) + div->max_div = 0XFFFF; + else + div->max_div = ret_payload[1]; + hw = &div->hw; ret = clk_hw_register(NULL, hw); if (ret) { diff --git a/drivers/clk/zynqmp/pll.c b/drivers/clk/zynqmp/pll.c index 18fee827602a..4036f5c01fde 100644 --- a/drivers/clk/zynqmp/pll.c +++ b/drivers/clk/zynqmp/pll.c @@ -98,26 +98,25 @@ static long zynqmp_pll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { u32 fbdiv; - long rate_div, f; + u32 mult, div; - /* Enable the fractional mode if needed */ - rate_div = (rate * FRAC_DIV) / *prate; - f = rate_div % FRAC_DIV; - if (f) { - if (rate > PS_PLL_VCO_MAX) { - fbdiv = rate / PS_PLL_VCO_MAX; - rate = rate / (fbdiv + 1); - } - if (rate < PS_PLL_VCO_MIN) { - fbdiv = DIV_ROUND_UP(PS_PLL_VCO_MIN, rate); - rate = rate * fbdiv; - } - return rate; + /* Let rate fall inside the range PS_PLL_VCO_MIN ~ PS_PLL_VCO_MAX */ + if (rate > PS_PLL_VCO_MAX) { + div = DIV_ROUND_UP(rate, PS_PLL_VCO_MAX); + rate = rate / div; + } + if (rate < PS_PLL_VCO_MIN) { + mult = DIV_ROUND_UP(PS_PLL_VCO_MIN, rate); + rate = rate * mult; } fbdiv = DIV_ROUND_CLOSEST(rate, *prate); - fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX); - return *prate * fbdiv; + if (fbdiv < PLL_FBDIV_MIN || fbdiv > PLL_FBDIV_MAX) { + fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX); + rate = *prate * fbdiv; + } + + return rate; } /** @@ -188,9 +187,12 @@ static int zynqmp_pll_set_rate(struct clk_hw *hw, unsigned long rate, frac = (parent_rate * f) / FRAC_DIV; ret = eemi_ops->clock_setdivider(clk_id, m); - if (ret) - pr_warn_once("%s() set divider failed for %s, ret = %d\n", - __func__, clk_name, ret); + if (ret) { + if (ret == -EUSERS) + WARN(1, "More than allowed devices are using the %s, which is forbidden\n", clk_name); + pr_err("%s() set divider failed for %s, ret = %d\n", + __func__, clk_name, ret); + } eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_DATA, clk_id, f, NULL); |