summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@kernel.org>2018-06-04 12:27:02 -0700
committerStephen Boyd <sboyd@kernel.org>2018-06-04 12:27:02 -0700
commit43705f52946bba973eeba75f1e92cf034e0ad03c (patch)
tree9ec089c0c99f84efb3ec5db95e716a6013f4272e
parent101cfc9f787d8ca22866a178f2c1684fb10f8c7a (diff)
parentd85d20053e1954ede2c731f3abaf507fdaa4911a (diff)
Merge branch 'clk-actions' into clk-next
* clk-actions: clk: actions: Add S900 SoC clock support clk: actions: Add pll clock support clk: actions: Add composite clock support clk: actions: Add fixed factor clock support clk: actions: Add factor clock support clk: actions: Add divider clock support clk: actions: Add mux clock support clk: actions: Add gate clock support clk: actions: Add common clock driver support dt-bindings: clock: Add Actions S900 clock bindings
-rw-r--r--Documentation/devicetree/bindings/clock/actions,s900-cmu.txt47
-rw-r--r--drivers/clk/Kconfig1
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/actions/Kconfig14
-rw-r--r--drivers/clk/actions/Makefile12
-rw-r--r--drivers/clk/actions/owl-common.c89
-rw-r--r--drivers/clk/actions/owl-common.h41
-rw-r--r--drivers/clk/actions/owl-composite.c199
-rw-r--r--drivers/clk/actions/owl-composite.h124
-rw-r--r--drivers/clk/actions/owl-divider.c94
-rw-r--r--drivers/clk/actions/owl-divider.h75
-rw-r--r--drivers/clk/actions/owl-factor.c222
-rw-r--r--drivers/clk/actions/owl-factor.h83
-rw-r--r--drivers/clk/actions/owl-fixed-factor.h28
-rw-r--r--drivers/clk/actions/owl-gate.c77
-rw-r--r--drivers/clk/actions/owl-gate.h73
-rw-r--r--drivers/clk/actions/owl-mux.c60
-rw-r--r--drivers/clk/actions/owl-mux.h61
-rw-r--r--drivers/clk/actions/owl-pll.c194
-rw-r--r--drivers/clk/actions/owl-pll.h92
-rw-r--r--drivers/clk/actions/owl-s900.c721
-rw-r--r--include/dt-bindings/clock/actions,s900-cmu.h129
22 files changed, 2437 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/clock/actions,s900-cmu.txt b/Documentation/devicetree/bindings/clock/actions,s900-cmu.txt
new file mode 100644
index 000000000000..93e4fb827cd6
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/actions,s900-cmu.txt
@@ -0,0 +1,47 @@
+* Actions S900 Clock Management Unit (CMU)
+
+The Actions S900 clock management unit generates and supplies clock to various
+controllers within the SoC. The clock binding described here is applicable to
+S900 SoC.
+
+Required Properties:
+
+- compatible: should be "actions,s900-cmu"
+- reg: physical base address of the controller and length of memory mapped
+ region.
+- clocks: Reference to the parent clocks ("hosc", "losc")
+- #clock-cells: should be 1.
+
+Each clock is assigned an identifier, and client nodes can use this identifier
+to specify the clock which they consume.
+
+All available clocks are defined as preprocessor macros in
+dt-bindings/clock/actions,s900-cmu.h header and can be used in device
+tree sources.
+
+External clocks:
+
+The hosc clock used as input for the plls is generated outside the SoC. It is
+expected that it is defined using standard clock bindings as "hosc".
+
+Actions S900 CMU also requires one more clock:
+ - "losc" - internal low frequency oscillator
+
+Example: Clock Management Unit node:
+
+ cmu: clock-controller@e0160000 {
+ compatible = "actions,s900-cmu";
+ reg = <0x0 0xe0160000 0x0 0x1000>;
+ clocks = <&hosc>, <&losc>;
+ #clock-cells = <1>;
+ };
+
+Example: UART controller node that consumes clock generated by the clock
+management unit:
+
+ uart: serial@e012a000 {
+ compatible = "actions,s900-uart", "actions,owl-uart";
+ reg = <0x0 0xe012a000 0x0 0x2000>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cmu CLK_UART5>;
+ };
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 41492e980ef4..24a5bc3a2ddb 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -279,6 +279,7 @@ config COMMON_CLK_STM32H7
---help---
Support for stm32h7 SoC family clocks
+source "drivers/clk/actions/Kconfig"
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
source "drivers/clk/imgtec/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index de6d06ac790b..35896e2ae95a 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
# please keep this section sorted lexicographically by directory path name
+obj-y += actions/
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
obj-$(CONFIG_ARCH_ARTPEC) += axis/
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
diff --git a/drivers/clk/actions/Kconfig b/drivers/clk/actions/Kconfig
new file mode 100644
index 000000000000..8854adb37847
--- /dev/null
+++ b/drivers/clk/actions/Kconfig
@@ -0,0 +1,14 @@
+config CLK_ACTIONS
+ bool "Clock driver for Actions Semi SoCs"
+ depends on ARCH_ACTIONS || COMPILE_TEST
+ default ARCH_ACTIONS
+
+if CLK_ACTIONS
+
+# SoC Drivers
+
+config CLK_OWL_S900
+ bool "Support for the Actions Semi OWL S900 clocks"
+ depends on (ARM64 && ARCH_ACTIONS) || COMPILE_TEST
+ default ARM64 && ARCH_ACTIONS
+endif
diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile
new file mode 100644
index 000000000000..76e431434d10
--- /dev/null
+++ b/drivers/clk/actions/Makefile
@@ -0,0 +1,12 @@
+obj-$(CONFIG_CLK_ACTIONS) += clk-owl.o
+
+clk-owl-y += owl-common.o
+clk-owl-y += owl-gate.o
+clk-owl-y += owl-mux.o
+clk-owl-y += owl-divider.o
+clk-owl-y += owl-factor.o
+clk-owl-y += owl-composite.o
+clk-owl-y += owl-pll.o
+
+# SoC support
+obj-$(CONFIG_CLK_OWL_S900) += owl-s900.o
diff --git a/drivers/clk/actions/owl-common.c b/drivers/clk/actions/owl-common.c
new file mode 100644
index 000000000000..61c1071b5180
--- /dev/null
+++ b/drivers/clk/actions/owl-common.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// OWL common clock driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "owl-common.h"
+
+static const struct regmap_config owl_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x00cc,
+ .fast_io = true,
+};
+
+static void owl_clk_set_regmap(const struct owl_clk_desc *desc,
+ struct regmap *regmap)
+{
+ int i;
+ struct owl_clk_common *clks;
+
+ for (i = 0; i < desc->num_clks; i++) {
+ clks = desc->clks[i];
+ if (!clks)
+ continue;
+
+ clks->regmap = regmap;
+ }
+}
+
+int owl_clk_regmap_init(struct platform_device *pdev,
+ const struct owl_clk_desc *desc)
+{
+ void __iomem *base;
+ struct regmap *regmap;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, base, &owl_regmap_config);
+ if (IS_ERR(regmap)) {
+ pr_err("failed to init regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ owl_clk_set_regmap(desc, regmap);
+
+ return 0;
+}
+
+int owl_clk_probe(struct device *dev, struct clk_hw_onecell_data *hw_clks)
+{
+ int i, ret;
+ struct clk_hw *hw;
+
+ for (i = 0; i < hw_clks->num; i++) {
+
+ hw = hw_clks->hws[i];
+
+ if (IS_ERR_OR_NULL(hw))
+ continue;
+
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret) {
+ dev_err(dev, "Couldn't register clock %d - %s\n",
+ i, hw->init->name);
+ return ret;
+ }
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_clks);
+ if (ret)
+ dev_err(dev, "Failed to add clock provider\n");
+
+ return ret;
+}
diff --git a/drivers/clk/actions/owl-common.h b/drivers/clk/actions/owl-common.h
new file mode 100644
index 000000000000..4fd726ec54a6
--- /dev/null
+++ b/drivers/clk/actions/owl-common.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// OWL common clock driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#ifndef _OWL_COMMON_H_
+#define _OWL_COMMON_H_
+
+#include <linux/clk-provider.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+struct device_node;
+
+struct owl_clk_common {
+ struct regmap *regmap;
+ struct clk_hw hw;
+};
+
+struct owl_clk_desc {
+ struct owl_clk_common **clks;
+ unsigned long num_clks;
+ struct clk_hw_onecell_data *hw_clks;
+};
+
+static inline struct owl_clk_common *
+ hw_to_owl_clk_common(const struct clk_hw *hw)
+{
+ return container_of(hw, struct owl_clk_common, hw);
+}
+
+int owl_clk_regmap_init(struct platform_device *pdev,
+ const struct owl_clk_desc *desc);
+int owl_clk_probe(struct device *dev, struct clk_hw_onecell_data *hw_clks);
+
+#endif /* _OWL_COMMON_H_ */
diff --git a/drivers/clk/actions/owl-composite.c b/drivers/clk/actions/owl-composite.c
new file mode 100644
index 000000000000..101706e0c66f
--- /dev/null
+++ b/drivers/clk/actions/owl-composite.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// OWL composite clock driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+
+#include "owl-composite.h"
+
+static u8 owl_comp_get_parent(struct clk_hw *hw)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+
+ return owl_mux_helper_get_parent(&comp->common, &comp->mux_hw);
+}
+
+static int owl_comp_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+
+ return owl_mux_helper_set_parent(&comp->common, &comp->mux_hw, index);
+}
+
+static void owl_comp_disable(struct clk_hw *hw)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+ struct owl_clk_common *common = &comp->common;
+
+ owl_gate_set(common, &comp->gate_hw, false);
+}
+
+static int owl_comp_enable(struct clk_hw *hw)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+ struct owl_clk_common *common = &comp->common;
+
+ owl_gate_set(common, &comp->gate_hw, true);
+
+ return 0;
+}
+
+static int owl_comp_is_enabled(struct clk_hw *hw)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+ struct owl_clk_common *common = &comp->common;
+
+ return owl_gate_clk_is_enabled(common, &comp->gate_hw);
+}
+
+static long owl_comp_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+
+ return owl_divider_helper_round_rate(&comp->common, &comp->rate.div_hw,
+ rate, parent_rate);
+}
+
+static unsigned long owl_comp_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+
+ return owl_divider_helper_recalc_rate(&comp->common, &comp->rate.div_hw,
+ parent_rate);
+}
+
+static int owl_comp_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+
+ return owl_divider_helper_set_rate(&comp->common, &comp->rate.div_hw,
+ rate, parent_rate);
+}
+
+static long owl_comp_fact_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+
+ return owl_factor_helper_round_rate(&comp->common,
+ &comp->rate.factor_hw,
+ rate, parent_rate);
+}
+
+static unsigned long owl_comp_fact_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+
+ return owl_factor_helper_recalc_rate(&comp->common,
+ &comp->rate.factor_hw,
+ parent_rate);
+}
+
+static int owl_comp_fact_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+
+ return owl_factor_helper_set_rate(&comp->common,
+ &comp->rate.factor_hw,
+ rate, parent_rate);
+}
+
+static long owl_comp_fix_fact_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+ struct clk_fixed_factor *fix_fact_hw = &comp->rate.fix_fact_hw;
+
+ return comp->fix_fact_ops->round_rate(&fix_fact_hw->hw, rate, parent_rate);
+}
+
+static unsigned long owl_comp_fix_fact_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct owl_composite *comp = hw_to_owl_comp(hw);
+ struct clk_fixed_factor *fix_fact_hw = &comp->rate.fix_fact_hw;
+
+ return comp->fix_fact_ops->recalc_rate(&fix_fact_hw->hw, parent_rate);
+
+}
+
+static int owl_comp_fix_fact_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ /*
+ * We must report success but we can do so unconditionally because
+ * owl_comp_fix_fact_round_rate returns values that ensure this call is
+ * a nop.
+ */
+
+ return 0;
+}
+
+const struct clk_ops owl_comp_div_ops = {
+ /* mux_ops */
+ .get_parent = owl_comp_get_parent,
+ .set_parent = owl_comp_set_parent,
+
+ /* gate_ops */
+ .disable = owl_comp_disable,
+ .enable = owl_comp_enable,
+ .is_enabled = owl_comp_is_enabled,
+
+ /* div_ops */
+ .round_rate = owl_comp_div_round_rate,
+ .recalc_rate = owl_comp_div_recalc_rate,
+ .set_rate = owl_comp_div_set_rate,
+};
+
+
+const struct clk_ops owl_comp_fact_ops = {
+ /* mux_ops */
+ .get_parent = owl_comp_get_parent,
+ .set_parent = owl_comp_set_parent,
+
+ /* gate_ops */
+ .disable = owl_comp_disable,
+ .enable = owl_comp_enable,
+ .is_enabled = owl_comp_is_enabled,
+
+ /* fact_ops */
+ .round_rate = owl_comp_fact_round_rate,
+ .recalc_rate = owl_comp_fact_recalc_rate,
+ .set_rate = owl_comp_fact_set_rate,
+};
+
+const struct clk_ops owl_comp_fix_fact_ops = {
+ /* gate_ops */
+ .disable = owl_comp_disable,
+ .enable = owl_comp_enable,
+ .is_enabled = owl_comp_is_enabled,
+
+ /* fix_fact_ops */
+ .round_rate = owl_comp_fix_fact_round_rate,
+ .recalc_rate = owl_comp_fix_fact_recalc_rate,
+ .set_rate = owl_comp_fix_fact_set_rate,
+};
+
+
+const struct clk_ops owl_comp_pass_ops = {
+ /* mux_ops */
+ .get_parent = owl_comp_get_parent,
+ .set_parent = owl_comp_set_parent,
+
+ /* gate_ops */
+ .disable = owl_comp_disable,
+ .enable = owl_comp_enable,
+ .is_enabled = owl_comp_is_enabled,
+};
diff --git a/drivers/clk/actions/owl-composite.h b/drivers/clk/actions/owl-composite.h
new file mode 100644
index 000000000000..b410ed5bf308
--- /dev/null
+++ b/drivers/clk/actions/owl-composite.h
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// OWL composite clock driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#ifndef _OWL_COMPOSITE_H_
+#define _OWL_COMPOSITE_H_
+
+#include "owl-common.h"
+#include "owl-mux.h"
+#include "owl-gate.h"
+#include "owl-factor.h"
+#include "owl-fixed-factor.h"
+#include "owl-divider.h"
+
+union owl_rate {
+ struct owl_divider_hw div_hw;
+ struct owl_factor_hw factor_hw;
+ struct clk_fixed_factor fix_fact_hw;
+};
+
+struct owl_composite {
+ struct owl_mux_hw mux_hw;
+ struct owl_gate_hw gate_hw;
+ union owl_rate rate;
+
+ const struct clk_ops *fix_fact_ops;
+
+ struct owl_clk_common common;
+};
+
+#define OWL_COMP_DIV(_struct, _name, _parent, \
+ _mux, _gate, _div, _flags) \
+ struct owl_composite _struct = { \
+ .mux_hw = _mux, \
+ .gate_hw = _gate, \
+ .rate.div_hw = _div, \
+ .common = { \
+ .regmap = NULL, \
+ .hw.init = CLK_HW_INIT_PARENTS(_name, \
+ _parent, \
+ &owl_comp_div_ops,\
+ _flags), \
+ }, \
+ }
+
+#define OWL_COMP_DIV_FIXED(_struct, _name, _parent, \
+ _gate, _div, _flags) \
+ struct owl_composite _struct = { \
+ .gate_hw = _gate, \
+ .rate.div_hw = _div, \
+ .common = { \
+ .regmap = NULL, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &owl_comp_div_ops,\
+ _flags), \
+ }, \
+ }
+
+#define OWL_COMP_FACTOR(_struct, _name, _parent, \
+ _mux, _gate, _factor, _flags) \
+ struct owl_composite _struct = { \
+ .mux_hw = _mux, \
+ .gate_hw = _gate, \
+ .rate.factor_hw = _factor, \
+ .common = { \
+ .regmap = NULL, \
+ .hw.init = CLK_HW_INIT_PARENTS(_name, \
+ _parent, \
+ &owl_comp_fact_ops,\
+ _flags), \
+ }, \
+ }
+
+#define OWL_COMP_FIXED_FACTOR(_struct, _name, _parent, \
+ _gate, _mul, _div, _flags) \
+ struct owl_composite _struct = { \
+ .gate_hw = _gate, \
+ .rate.fix_fact_hw.mult = _mul, \
+ .rate.fix_fact_hw.div = _div, \
+ .fix_fact_ops = &clk_fixed_factor_ops, \
+ .common = { \
+ .regmap = NULL, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &owl_comp_fix_fact_ops,\
+ _flags), \
+ }, \
+ }
+
+#define OWL_COMP_PASS(_struct, _name, _parent, \
+ _mux, _gate, _flags) \
+ struct owl_composite _struct = { \
+ .mux_hw = _mux, \
+ .gate_hw = _gate, \
+ .common = { \
+ .regmap = NULL, \
+ .hw.init = CLK_HW_INIT_PARENTS(_name, \
+ _parent, \
+ &owl_comp_pass_ops,\
+ _flags), \
+ }, \
+ }
+
+static inline struct owl_composite *hw_to_owl_comp(const struct clk_hw *hw)
+{
+ struct owl_clk_common *common = hw_to_owl_clk_common(hw);
+
+ return container_of(common, struct owl_composite, common);
+}
+
+extern const struct clk_ops owl_comp_div_ops;
+extern const struct clk_ops owl_comp_fact_ops;
+extern const struct clk_ops owl_comp_fix_fact_ops;
+extern const struct clk_ops owl_comp_pass_ops;
+extern const struct clk_ops clk_fixed_factor_ops;
+
+#endif /* _OWL_COMPOSITE_H_ */
diff --git a/drivers/clk/actions/owl-divider.c b/drivers/clk/actions/owl-divider.c
new file mode 100644
index 000000000000..cddac00fe324
--- /dev/null
+++ b/drivers/clk/actions/owl-divider.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// OWL divider clock driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+
+#include "owl-divider.h"
+
+long owl_divider_helper_round_rate(struct owl_clk_common *common,
+ const struct owl_divider_hw *div_hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return divider_round_rate(&common->hw, rate, parent_rate,
+ div_hw->table, div_hw->width,
+ div_hw->div_flags);
+}
+
+static long owl_divider_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct owl_divider *div = hw_to_owl_divider(hw);
+
+ return owl_divider_helper_round_rate(&div->common, &div->div_hw,
+ rate, parent_rate);
+}
+
+unsigned long owl_divider_helper_recalc_rate(struct owl_clk_common *common,
+ const struct owl_divider_hw *div_hw,
+ unsigned long parent_rate)
+{
+ unsigned long val;
+ unsigned int reg;
+
+ regmap_read(common->regmap, div_hw->reg, &reg);
+ val = reg >> div_hw->shift;
+ val &= (1 << div_hw->width) - 1;
+
+ return divider_recalc_rate(&common->hw, parent_rate,
+ val, div_hw->table,
+ div_hw->div_flags,
+ div_hw->width);
+}
+
+static unsigned long owl_divider_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct owl_divider *div = hw_to_owl_divider(hw);
+
+ return owl_divider_helper_recalc_rate(&div->common,
+ &div->div_hw, parent_rate);
+}
+
+int owl_divider_helper_set_rate(const struct owl_clk_common *common,
+ const struct owl_divider_hw *div_hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ unsigned long val;
+ unsigned int reg;
+
+ val = divider_get_val(rate, parent_rate, div_hw->table,
+ div_hw->width, 0);
+
+ regmap_read(common->regmap, div_hw->reg, &reg);
+ reg &= ~GENMASK(div_hw->width + div_hw->shift - 1, div_hw->shift);
+
+ regmap_write(common->regmap, div_hw->reg,
+ reg | (val << div_hw->shift));
+
+ return 0;
+}
+
+static int owl_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct owl_divider *div = hw_to_owl_divider(hw);
+
+ return owl_divider_helper_set_rate(&div->common, &div->div_hw,
+ rate, parent_rate);
+}
+
+const struct clk_ops owl_divider_ops = {
+ .recalc_rate = owl_divider_recalc_rate,
+ .round_rate = owl_divider_round_rate,
+ .set_rate = owl_divider_set_rate,
+};
diff --git a/drivers/clk/actions/owl-divider.h b/drivers/clk/actions/owl-divider.h
new file mode 100644
index 000000000000..92d3e3d23967
--- /dev/null
+++ b/drivers/clk/actions/owl-divider.h
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// OWL divider clock driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#ifndef _OWL_DIVIDER_H_
+#define _OWL_DIVIDER_H_
+
+#include "owl-common.h"
+
+struct owl_divider_hw {
+ u32 reg;
+ u8 shift;
+ u8 width;
+ u8 div_flags;
+ struct clk_div_table *table;
+};
+
+struct owl_divider {
+ struct owl_divider_hw div_hw;
+ struct owl_clk_common common;
+};
+
+#define OWL_DIVIDER_HW(_reg, _shift, _width, _div_flags, _table) \
+ { \
+ .reg = _reg, \
+ .shift = _shift, \
+ .width = _width, \
+ .div_flags = _div_flags, \
+ .table = _table, \
+ }
+
+#define OWL_DIVIDER(_struct, _name, _parent, _reg, \
+ _shift, _width, _table, _div_flags, _flags) \
+ struct owl_divider _struct = { \
+ .div_hw = OWL_DIVIDER_HW(_reg, _shift, _width, \
+ _div_flags, _table), \
+ .common = { \
+ .regmap = NULL, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &owl_divider_ops, \
+ _flags), \
+ }, \
+ }
+
+static inline struct owl_divider *hw_to_owl_divider(const struct clk_hw *hw)
+{
+ struct owl_clk_common *common = hw_to_owl_clk_common(hw);
+
+ return container_of(common, struct owl_divider, common);
+}
+
+long owl_divider_helper_round_rate(struct owl_clk_common *common,
+ const struct owl_divider_hw *div_hw,
+ unsigned long rate,
+ unsigned long *parent_rate);
+
+unsigned long owl_divider_helper_recalc_rate(struct owl_clk_common *common,
+ const struct owl_divider_hw *div_hw,
+ unsigned long parent_rate);
+
+int owl_divider_helper_set_rate(const struct owl_clk_common *common,
+ const struct owl_divider_hw *div_hw,
+ unsigned long rate,
+ unsigned long parent_rate);
+
+extern const struct clk_ops owl_divider_ops;
+
+#endif /* _OWL_DIVIDER_H_ */
diff --git a/drivers/clk/actions/owl-factor.c b/drivers/clk/actions/owl-factor.c
new file mode 100644
index 000000000000..317d4a9e112e
--- /dev/null
+++ b/drivers/clk/actions/owl-factor.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// OWL factor clock driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "owl-factor.h"
+
+static unsigned int _get_table_maxval(const struct clk_factor_table *table)
+{
+ unsigned int maxval = 0;
+ const struct clk_factor_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val > maxval)
+ maxval = clkt->val;
+ return maxval;
+}
+
+static int _get_table_div_mul(const struct clk_factor_table *table,
+ unsigned int val, unsigned int *mul, unsigned int *div)
+{
+ const struct clk_factor_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++) {
+ if (clkt->val == val) {
+ *mul = clkt->mul;
+ *div = clkt->div;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int _get_table_val(const struct clk_factor_table *table,
+ unsigned long rate, unsigned long parent_rate)
+{
+ const struct clk_factor_table *clkt;
+ int val = -1;
+ u64 calc_rate;
+
+ for (clkt = table; clkt->div; clkt++) {
+ calc_rate = parent_rate * clkt->mul;
+ do_div(calc_rate, clkt->div);
+
+ if ((unsigned long)calc_rate <= rate) {
+ val = clkt->val;
+ break;
+ }
+ }
+
+ if (val == -1)
+ val = _get_table_maxval(table);
+
+ return val;
+}
+
+static int clk_val_best(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate)
+{
+ struct owl_factor *factor = hw_to_owl_factor(hw);
+ struct owl_factor_hw *factor_hw = &factor->factor_hw;
+ const struct clk_factor_table *clkt = factor_hw->table;
+ unsigned long parent_rate, try_parent_rate, best = 0, cur_rate;
+ unsigned long parent_rate_saved = *best_parent_rate;
+ int bestval = 0;
+
+ if (!rate)
+ rate = 1;
+
+ if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
+ parent_rate = *best_parent_rate;
+ bestval = _get_table_val(clkt, rate, parent_rate);
+ return bestval;
+ }
+
+ for (clkt = factor_hw->table; clkt->div; clkt++) {
+ try_parent_rate = rate * clkt->div / clkt->mul;
+
+ if (try_parent_rate == parent_rate_saved) {
+ pr_debug("%s: [%d %d %d] found try_parent_rate %ld\n",
+ __func__, clkt->val, clkt->mul, clkt->div,
+ try_parent_rate);
+ /*
+ * It's the most ideal case if the requested rate can be
+ * divided from parent clock without any need to change
+ * parent rate, so return the divider immediately.
+ */
+ *best_parent_rate = parent_rate_saved;
+ return clkt->val;
+ }
+
+ parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
+ try_parent_rate);
+ cur_rate = DIV_ROUND_UP(parent_rate, clkt->div) * clkt->mul;
+ if (cur_rate <= rate && cur_rate > best) {
+ bestval = clkt->val;
+ best = cur_rate;
+ *best_parent_rate = parent_rate;
+ }
+ }
+
+ if (!bestval) {
+ bestval = _get_table_maxval(clkt);
+ *best_parent_rate = clk_hw_round_rate(
+ clk_hw_get_parent(hw), 1);
+ }
+
+ return bestval;
+}
+
+long owl_factor_helper_round_rate(struct owl_clk_common *common,
+ const struct owl_factor_hw *factor_hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ const struct clk_factor_table *clkt = factor_hw->table;
+ unsigned int val, mul = 0, div = 1;
+
+ val = clk_val_best(&common->hw, rate, parent_rate);
+ _get_table_div_mul(clkt, val, &mul, &div);
+
+ return *parent_rate * mul / div;
+}
+
+static long owl_factor_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct owl_factor *factor = hw_to_owl_factor(hw);
+ struct owl_factor_hw *factor_hw = &factor->factor_hw;
+
+ return owl_factor_helper_round_rate(&factor->common, factor_hw,
+ rate, parent_rate);
+}
+
+unsigned long owl_factor_helper_recalc_rate(struct owl_clk_common *common,
+ const struct owl_factor_hw *factor_hw,
+ unsigned long parent_rate)
+{
+ const struct clk_factor_table *clkt = factor_hw->table;
+ unsigned long long int rate;
+ u32 reg, val, mul, div;
+
+ div = 0;
+ mul = 0;
+
+ regmap_read(common->regmap, factor_hw->reg, &reg);
+
+ val = reg >> factor_hw->shift;
+ val &= div_mask(factor_hw);
+
+ _get_table_div_mul(clkt, val, &mul, &div);
+ if (!div) {
+ WARN(!(factor_hw->fct_flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ __clk_get_name(common->hw.clk));
+ return parent_rate;
+ }
+
+ rate = (unsigned long long int)parent_rate * mul;
+ do_div(rate, div);
+
+ return rate;
+}
+
+static unsigned long owl_factor_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct owl_factor *factor = hw_to_owl_factor(hw);
+ struct owl_factor_hw *factor_hw = &factor->factor_hw;
+ struct owl_clk_common *common = &factor->common;
+
+ return owl_factor_helper_recalc_rate(common, factor_hw, parent_rate);
+}
+
+int owl_factor_helper_set_rate(const struct owl_clk_common *common,
+ const struct owl_factor_hw *factor_hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 val, reg;
+
+ val = _get_table_val(factor_hw->table, rate, parent_rate);
+
+ if (val > div_mask(factor_hw))
+ val = div_mask(factor_hw);
+
+ regmap_read(common->regmap, factor_hw->reg, &reg);
+
+ reg &= ~(div_mask(factor_hw) << factor_hw->shift);
+ reg |= val << factor_hw->shift;
+
+ regmap_write(common->regmap, factor_hw->reg, reg);
+
+ return 0;
+}
+
+static int owl_factor_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsign