Allow defining fractional dividers that can't be set.
Signed-off-by: Alban Bedel <[email protected]>
---
drivers/clk/clk-fractional-divider.c | 10 +++++++++-
include/linux/clk-provider.h | 3 +++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index 82a59d0..b562281 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -96,6 +96,11 @@ const struct clk_ops clk_fractional_divider_ops = {
};
EXPORT_SYMBOL_GPL(clk_fractional_divider_ops);
+const struct clk_ops clk_fractional_divider_ro_ops = {
+ .recalc_rate = clk_fd_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fractional_divider_ro_ops);
+
struct clk *clk_register_fractional_divider(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
@@ -112,7 +117,10 @@ struct clk *clk_register_fractional_divider(struct device *dev,
}
init.name = name;
- init.ops = &clk_fractional_divider_ops;
+ if (clk_divider_flags & CLK_FRACTIONAL_DIVIDER_READ_ONLY)
+ init.ops = &clk_fractional_divider_ro_ops;
+ else
+ init.ops = &clk_fractional_divider_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index be21af1..29ea77a 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -462,7 +462,10 @@ struct clk_fractional_divider {
spinlock_t *lock;
};
+#define CLK_FRACTIONAL_DIVIDER_READ_ONLY BIT(0)
+
extern const struct clk_ops clk_fractional_divider_ops;
+extern const struct clk_ops clk_fractional_divider_ro_ops;
struct clk *clk_register_fractional_divider(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
--
2.0.0
Many divider hardware use a (n+1) scheme to prevent a zero divider.
Signed-off-by: Alban Bedel <[email protected]>
---
drivers/clk/clk-fractional-divider.c | 4 ++++
include/linux/clk-provider.h | 1 +
2 files changed, 5 insertions(+)
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index b562281..bcee7f7d3 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -35,6 +35,8 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
m = (val & fd->mmask) >> fd->mshift;
n = (val & fd->nmask) >> fd->nshift;
+ if (fd->flags & CLK_FRACTIONAL_DIVIDER_DIVISOR_PLUS_ONE)
+ n += 1;
ret = (u64)parent_rate * m;
do_div(ret, n);
@@ -74,6 +76,8 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
div = gcd(parent_rate, rate);
m = rate / div;
n = parent_rate / div;
+ if (fd->flags & CLK_FRACTIONAL_DIVIDER_DIVISOR_PLUS_ONE)
+ n -= 1;
if (fd->lock)
spin_lock_irqsave(fd->lock, flags);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 29ea77a..00013b1 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -463,6 +463,7 @@ struct clk_fractional_divider {
};
#define CLK_FRACTIONAL_DIVIDER_READ_ONLY BIT(0)
+#define CLK_FRACTIONAL_DIVIDER_DIVISOR_PLUS_ONE BIT(1)
extern const struct clk_ops clk_fractional_divider_ops;
extern const struct clk_ops clk_fractional_divider_ro_ops;
--
2.0.0
Signed-off-by: Alban Bedel <[email protected]>
---
.../bindings/clock/mmio-clock-controller.txt | 127 +++++++++++++++++++++
1 file changed, 127 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/mmio-clock-controller.txt
diff --git a/Documentation/devicetree/bindings/clock/mmio-clock-controller.txt b/Documentation/devicetree/bindings/clock/mmio-clock-controller.txt
new file mode 100644
index 0000000..5a5f752
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/mmio-clock-controller.txt
@@ -0,0 +1,127 @@
+Binding for a generic MMIO clock controller
+
+This binding allow describing simple MMIO clock controllers using a few
+primitive blocks like dividers and gates.
+
+Required properties:
+- compatible : shall be "mmio-clock-controller"
+- reg : address and size of the register area
+- #address-cells : shall be 1
+- #size-cells : shall be 0
+
+Children nodes are used to represent the blocks found in the controller.
+Beside the children types described here it is also possible to use fixed
+clocks[1] and dividers[2].
+
+All the following bindings use the common clock binding[3].
+
+[1] Documentation/devicetree/bindings/clock/fixed-clock.txt
+[2] Documentation/devicetree/bindings/clock/fixed-factor-clock.txt
+[3] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+- gate
+
+ Required properties:
+ - compatible : shall be "gate-clock"
+ - reg : offset of the register
+ - #clock-cells : from common clock binding; shall be set to 0
+ - clocks : from common clock binding; the parent clock
+ - clock-gate-bit : position of the gating bit inside the register
+
+ Optional properties:
+ - clock-output-names : from common clock binding
+ - high-word-mask : set if the high word is a write mask
+ - low-active : set if the bit should be cleared to enable the clock
+
+- mux
+
+ Required properties:
+ - compatible : shall be "mux-clock"
+ - reg : offset of the register
+ - #clock-cells : from common clock binding; shall be set to 0
+ - clocks : from common clock binding; the parent clocks
+ - clock-mux-shift : position of the mux field inside the register
+ - clock-mux-width : width of the mux field in bits
+
+ Optional properties:
+ - clock-output-names : from common clock binding
+ - read-only : set if the muxer should not be writable
+ - index-plus-one : set if the clock index should be offset by one
+ - index-bitmask : set if the mux field is a bit mask
+ - high-word-mask : set if the high word is a write mask
+
+- divider
+
+ Required properties:
+ - compatible : shall be "divider-clock"
+ - reg : offset of the register
+ - #clock-cells : from common clock binding; shall be set to 0
+ - clocks : from common clock binding; the parent clock
+ - clock-div-shift : position of the divider field inside the register
+ - clock-div-width : width of the divider field in bits
+
+ Optional properties:
+ - clock-output-names : from common clock binding
+ - clock-mult-shift : position of the multiplier field inside the register
+ - clock-mult-width : width of the multiplier field in bits
+ - read-only : set if the divider should not be writable
+ - divider-plus-one : set if the divider should be offset by one
+
+ Optional properties only supported without multiplier:
+ - divider-power-two : if set the divisor is 2 raised to the value read from
+ the hardware register
+ - divider-allow-zero : set to allow setting the divider to zero
+ - high-word-mask : set if the high word is a write mask
+
+Example:
+ clkctrl@8ABC0000 {
+ compatible = "mmio-clock-controller";
+ reg = <0x8ABC0000 0x10>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pll: pll {
+ compatible = "divider-clock";
+ reg = <0>;
+
+ clock-names = "external-oscillator";
+ clocks = <&ext_osc>;
+
+ #clock-cells = <0>;
+ clock-output-names = "pll";
+
+ clock-mult-shift = <0>;
+ clock-mult-width = <10>;
+
+ clock-div-shift = <10>;
+ clock-div-width = <6>;
+ };
+
+ pll_bypass: pll_bypass {
+ compatible = "mux-clock";
+ reg = <0>;
+
+ clock-names = "pll", "external-oscillator";
+ clocks = <&pll>, <&ext_osc>;
+
+ #clock-cells = <0>;
+ clock-output-names = "pll-bypass";
+
+ clock-mux-shift = <16>;
+ clock-mux-width = <1>;
+ };
+
+ clk1: pll_gate {
+ compatible = "gate-clock";
+ reg = <0>;
+
+ clock-names = "pll-bypass";
+ clocks = <&pll_bypass>;
+
+ #clock-cells = <0>;
+ clock-output-names = "clk1";
+
+ clock-gate-bit = <17>;
+ };
+ };
--
2.0.0
This driver allow defining simple MMIO clock controllers using only DT
nodes. The clock controller driver map the configured MMIO range and
allow creating simple clock trees using dividers, gates and muxers.
Signed-off-by: Alban Bedel <[email protected]>
---
drivers/clk/Kconfig | 9 ++
drivers/clk/Makefile | 1 +
drivers/clk/clk-mmio-controller.c | 208 ++++++++++++++++++++++++++++++++++++++
3 files changed, 218 insertions(+)
create mode 100644 drivers/clk/clk-mmio-controller.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 455fd17..ca6b896 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -23,6 +23,15 @@ config COMMON_CLK
menu "Common Clock Framework"
depends on COMMON_CLK
+config COMMON_CLK_MMIO_CONTROLLER
+ tristate "Generic driver for simple MMIO clock controller"
+ depends on OF
+ ---help---
+ This driver allow supporting simple MMIO clock controllers that
+ only consist of dividers, muxes and gates. The clock tree and
+ register mapping can be fully configured from the devicetree
+ removing the need for a device specific driver.
+
config COMMON_CLK_WM831X
tristate "Clock driver for WM831x/2x PMICs"
depends on MFD_WM831X
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d5fba5b..0d9c8e4 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o
+obj-$(CONFIG_COMMON_CLK_MMIO_CONTROLLER)+= clk-mmio-controller.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
diff --git a/drivers/clk/clk-mmio-controller.c b/drivers/clk/clk-mmio-controller.c
new file mode 100644
index 0000000..0e5fb50
--- /dev/null
+++ b/drivers/clk/clk-mmio-controller.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014 Alban Bedel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+
+#define of_printk(node, lvl, fmt, ...) \
+ printk(lvl "%s: " fmt, node->full_name, ##__VA_ARGS__)
+
+#define of_err(node, ...) of_printk(node, KERN_ERR, __VA_ARGS__)
+#define of_warn(node, ...) of_printk(node, KERN_WARNING, __VA_ARGS__)
+
+typedef struct clk *(*mmio_clock_probe_t)(
+ struct device_node *np, void __iomem *reg, spinlock_t *lock);
+
+static struct clk *mux_clock_probe(
+ struct device_node *np, void __iomem *reg, spinlock_t *lock)
+{
+ const char *parents[32] = {};
+ unsigned int num_parents = 0;
+ const char *clk_name;
+ u8 clk_mux_flags = 0;
+ u32 width, shift;
+
+ clk_name = np->name;
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ /* Read the parents */
+ while (num_parents < ARRAY_SIZE(parents)) {
+ parents[num_parents] = of_clk_get_parent_name(np, num_parents);
+ if (parents[num_parents])
+ num_parents++;
+ else
+ break;
+ }
+
+ if (of_property_read_u32(np, "clock-mux-width", &width) || width > 31 ||
+ of_property_read_u32(np, "clock-mux-shift", &shift) || shift > 31)
+ return ERR_PTR(-EINVAL);
+
+ if (of_property_read_bool(np, "index-plus-one"))
+ clk_mux_flags |= CLK_MUX_INDEX_ONE;
+ if (of_property_read_bool(np, "index-bitmask"))
+ clk_mux_flags |= CLK_MUX_INDEX_BIT;
+ if (of_property_read_bool(np, "high-word-mask"))
+ clk_mux_flags |= CLK_MUX_HIWORD_MASK;
+ if (of_property_read_bool(np, "read-only"))
+ clk_mux_flags |= CLK_MUX_READ_ONLY;
+
+ return clk_register_mux_table(
+ NULL, clk_name, parents, num_parents, 0, reg,
+ shift, width, clk_mux_flags, NULL, lock);
+}
+
+static struct clk *gate_clock_probe(
+ struct device_node *np, void __iomem *reg, spinlock_t *lock)
+{
+ u8 clk_gate_flags = 0;
+ const char *clk_name;
+ u32 bit = 0;
+
+ clk_name = np->name;
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ if (of_property_read_u32(np, "clock-gate-bit", &bit) || bit > 31)
+ return ERR_PTR(-EINVAL);
+
+ if (of_property_read_bool(np, "high-word-mask"))
+ clk_gate_flags |= CLK_GATE_HIWORD_MASK;
+
+ return clk_register_gate(
+ NULL, clk_name, of_clk_get_parent_name(np, 0), 0, reg,
+ bit, clk_gate_flags, lock);
+}
+
+static struct clk *divider_clock_probe(
+ struct device_node *np, void __iomem *reg, spinlock_t *lock)
+{
+ u8 clk_divider_flags = 0;
+ const char *clk_name;
+ u32 div_shift = 0, div_width = 0;
+ u32 mul_shift = 0, mul_width = 0;
+
+ clk_name = np->name;
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ of_property_read_u32(np, "clock-div-shift", &div_shift);
+ of_property_read_u32(np, "clock-div-width", &div_width);
+ of_property_read_u32(np, "clock-mult-shift", &mul_shift);
+ of_property_read_u32(np, "clock-mult-width", &mul_width);
+
+ if (div_shift > 31 || div_width > 31 ||
+ mul_shift > 31 || mul_width > 31)
+ return ERR_PTR(-EINVAL);
+
+ if (mul_width && div_width) {
+ if (of_property_read_bool(np, "read-only"))
+ clk_divider_flags |= CLK_FRACTIONAL_DIVIDER_READ_ONLY;
+ if (of_property_read_bool(np, "divider-plus-one"))
+ clk_divider_flags |=
+ CLK_FRACTIONAL_DIVIDER_DIVISOR_PLUS_ONE;
+
+ return clk_register_fractional_divider(
+ NULL, clk_name, of_clk_get_parent_name(np, 0), 0, reg,
+ mul_shift, mul_width, div_shift, div_width,
+ clk_divider_flags, lock);
+ } else if (div_width) {
+ if (of_property_read_bool(np, "read-only"))
+ clk_divider_flags |= CLK_DIVIDER_READ_ONLY;
+ if (!of_property_read_bool(np, "divider-plus-one"))
+ clk_divider_flags |= CLK_DIVIDER_ONE_BASED;
+ if (of_property_read_bool(np, "divider-power-of-two"))
+ clk_divider_flags |= CLK_DIVIDER_POWER_OF_TWO;
+ if (of_property_read_bool(np, "divider-allow-zero"))
+ clk_divider_flags |= CLK_DIVIDER_ALLOW_ZERO;
+ if (of_property_read_bool(np, "high-word-mask"))
+ clk_divider_flags |= CLK_DIVIDER_HIWORD_MASK;
+ if (of_property_read_bool(np, "divider-roud-closest"))
+ clk_divider_flags |= CLK_DIVIDER_ROUND_CLOSEST;
+
+ return clk_register_divider(
+ NULL, clk_name, of_clk_get_parent_name(np, 0), 0, reg,
+ div_shift, div_width, clk_divider_flags, lock);
+ } else {
+ of_err(np, "Unsupported mode\n");
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+const struct of_device_id clk_mmio_controller_matches[] = {
+ {
+ .compatible = "mux-clock",
+ .data = mux_clock_probe,
+ },
+ {
+ .compatible = "gate-clock",
+ .data = gate_clock_probe,
+ },
+ {
+ .compatible = "divider-clock",
+ .data = divider_clock_probe,
+ },
+ { }
+};
+
+static void __init mmio_clock_controller_register(struct device_node *np)
+{
+ const struct of_device_id *match;
+ struct device_node *child;
+ mmio_clock_probe_t probe;
+ unsigned int count = 0;
+ void __iomem *base;
+ spinlock_t *lock;
+ struct clk *clk;
+ u32 reg;
+ int err;
+
+ lock = kzalloc(sizeof(*lock), GFP_KERNEL);
+ if (!lock)
+ return;
+ spin_lock_init(lock);
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ of_err(np, "failed to get IO memory\n");
+ kfree(lock);
+ return;
+ }
+
+ for_each_available_child_of_node(np, child) {
+ match = of_match_node(clk_mmio_controller_matches, child);
+ if (!match)
+ continue;
+
+ if (of_property_read_u32(child, "reg", ®)) {
+ of_warn(child, "the reg property is missing!\n");
+ continue;
+ }
+
+ probe = match->data;
+ clk = probe(child, base + reg, lock);
+ if (IS_ERR(clk)) {
+ of_err(child, "failed to register clock\n");
+ continue;
+ }
+
+ err = of_clk_add_provider(child, of_clk_src_simple_get, clk);
+ if (err) {
+ clk_unregister(clk);
+ of_err(child, "failed to add clock provider\n");
+ }
+ count += 1;
+ }
+
+ if (count == 0) {
+ iounmap(base);
+ kfree(lock);
+ }
+}
+CLK_OF_DECLARE(clk_generic, "mmio-clock-controller",
+ mmio_clock_controller_register);
--
2.0.0