aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig36
-rw-r--r--drivers/clk/Makefile4
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c8
-rw-r--r--drivers/clk/bcm/clk-iproc-pll.c12
-rw-r--r--drivers/clk/bcm/clk-raspberrypi.c2
-rw-r--r--drivers/clk/berlin/bg2.c5
-rw-r--r--drivers/clk/berlin/bg2q.c6
-rw-r--r--drivers/clk/clk-ast2600.c2
-rw-r--r--drivers/clk/clk-cdce925.c12
-rw-r--r--drivers/clk/clk-conf.c12
-rw-r--r--drivers/clk/clk-devres.c104
-rw-r--r--drivers/clk/clk-fixed-factor.c5
-rw-r--r--drivers/clk/clk-gate.c2
-rw-r--r--drivers/clk/clk-npcm7xx.c2
-rw-r--r--drivers/clk/clk-oxnas.c6
-rw-r--r--drivers/clk/clk-scmi.c1
-rw-r--r--drivers/clk/clk-si5324.c1227
-rw-r--r--drivers/clk/clk-si5324.h140
-rw-r--r--drivers/clk/clk-si5341.c4
-rw-r--r--drivers/clk/clk.c220
-rw-r--r--drivers/clk/hisilicon/clk-hi3519.c2
-rw-r--r--drivers/clk/hisilicon/clk-hi3620.c4
-rw-r--r--drivers/clk/idt/Makefile3
-rw-r--r--drivers/clk/idt/clk-idt8t49n24x-core.c933
-rw-r--r--drivers/clk/idt/clk-idt8t49n24x-core.h272
-rw-r--r--drivers/clk/idt/clk-idt8t49n24x-debugfs.c382
-rw-r--r--drivers/clk/idt/clk-idt8t49n24x-debugfs.h21
-rw-r--r--drivers/clk/idt/clk-idt8t49n24x.c641
-rw-r--r--drivers/clk/imx/Kconfig1
-rw-r--r--drivers/clk/imx/clk-composite-8m.c12
-rw-r--r--drivers/clk/imx/clk-imx6sx.c4
-rw-r--r--drivers/clk/imx/clk-imx8mm.c87
-rw-r--r--drivers/clk/imx/clk-imx8mn.c14
-rw-r--r--drivers/clk/imx/clk-pll14xx.c30
-rw-r--r--drivers/clk/imx/clk.h3
-rw-r--r--drivers/clk/ingenic/tcu.c15
-rw-r--r--drivers/clk/keystone/pll.c17
-rw-r--r--drivers/clk/keystone/sci-clk.c2
-rw-r--r--drivers/clk/mediatek/clk-mt2701.c8
-rw-r--r--drivers/clk/mediatek/clk-mt6779.c4
-rw-r--r--drivers/clk/mediatek/clk-mt6797.c6
-rw-r--r--drivers/clk/mediatek/clk-mt7629-eth.c4
-rw-r--r--drivers/clk/mediatek/clk-mt7629.c6
-rw-r--r--drivers/clk/mediatek/clk-mt8183-mfgcfg.c6
-rw-r--r--drivers/clk/mediatek/reset.c4
-rw-r--r--drivers/clk/meson/meson-aoclk.c5
-rw-r--r--drivers/clk/meson/meson-eeclk.c5
-rw-r--r--drivers/clk/meson/meson8b.c5
-rw-r--r--drivers/clk/mmp/clk-of-pxa168.c3
-rw-r--r--drivers/clk/qcom/camcc-sdm845.c4
-rw-r--r--drivers/clk/qcom/clk-krait.c9
-rw-r--r--drivers/clk/qcom/clk-rcg2.c14
-rw-r--r--drivers/clk/qcom/dispcc-sdm845.c2
-rw-r--r--drivers/clk/qcom/gcc-ipq8074.c27
-rw-r--r--drivers/clk/qcom/gcc-mdm9615.c2
-rw-r--r--drivers/clk/qcom/gcc-qcs404.c24
-rw-r--r--drivers/clk/qcom/gcc-sdm845.c1
-rw-r--r--drivers/clk/qcom/gcc-sm8150.c98
-rw-r--r--drivers/clk/qcom/gpucc-sdm845.c7
-rw-r--r--drivers/clk/qcom/mmcc-apq8084.c2
-rw-r--r--drivers/clk/qcom/mmcc-msm8974.c2
-rw-r--r--drivers/clk/qcom/reset.c33
-rw-r--r--drivers/clk/qcom/reset.h2
-rw-r--r--drivers/clk/renesas/r7s9210-cpg-mssr.c2
-rw-r--r--drivers/clk/renesas/r9a06g032-clocks.c11
-rw-r--r--drivers/clk/renesas/renesas-cpg-mssr.c29
-rw-r--r--drivers/clk/renesas/renesas-cpg-mssr.h12
-rw-r--r--drivers/clk/rockchip/clk-pll.c1
-rw-r--r--drivers/clk/rockchip/clk-rk3128.c2
-rw-r--r--drivers/clk/rockchip/clk-rk3188.c1
-rw-r--r--drivers/clk/rockchip/clk-rk3399.c2
-rw-r--r--drivers/clk/samsung/clk-pll.c1
-rw-r--r--drivers/clk/si5324.h68
-rw-r--r--drivers/clk/si5324drv.c382
-rw-r--r--drivers/clk/si5324drv.h100
-rw-r--r--drivers/clk/socfpga/clk-gate.c16
-rw-r--r--drivers/clk/socfpga/clk-periph.c8
-rw-r--r--drivers/clk/socfpga/clk-pll.c17
-rw-r--r--drivers/clk/st/clkgen-fsyn.c5
-rw-r--r--drivers/clk/sunxi-ng/ccu_mmc_timing.c2
-rw-r--r--drivers/clk/tegra/clk-bpmp.c2
-rw-r--r--drivers/clk/tegra/clk-emc.c2
-rw-r--r--drivers/clk/tegra/clk-tegra114.c1
-rw-r--r--drivers/clk/tegra/clk-tegra20.c29
-rw-r--r--drivers/clk/tegra/clk-tegra210.c1
-rw-r--r--drivers/clk/ti/clk-dra7-atl.c9
-rw-r--r--drivers/clk/zynq/clkc.c43
-rw-r--r--drivers/clk/zynqmp/clk-mux-zynqmp.c2
-rw-r--r--drivers/clk/zynqmp/clkc.c13
-rw-r--r--drivers/clk/zynqmp/divider.c99
-rw-r--r--drivers/clk/zynqmp/pll.c40
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, &reg, 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, &reg,
+ 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", &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);