Add driver for TZ1090 clock divider, which divides an input clock by an integer. Two policy decisions are made depending on the MMIO address of the divider: - The UART clock divider sets CLK_SET_RATE_PARENT so that clock changes can propagate up to the CLK_UART_SW mux which allows more precision to be achieved. - The Meta clock divider sets CLK_DIVIDER_READ_ONLY to prevent it being changed dynamically. This is normally set by the bootloader along with the system PLL and has a whole bunch of derivative peripheral clocks. Signed-off-by: James Hogan <james.hogan@xxxxxxxxxx> Cc: Mike Turquette <mturquette@xxxxxxxxxx> Cc: linux-metag@xxxxxxxxxxxxxxx --- drivers/clk/tz1090/Makefile | 1 + drivers/clk/tz1090/clk-tz1090-divider.c | 96 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 drivers/clk/tz1090/clk-tz1090-divider.c diff --git a/drivers/clk/tz1090/Makefile b/drivers/clk/tz1090/Makefile index 92e38a8..529c79c 100644 --- a/drivers/clk/tz1090/Makefile +++ b/drivers/clk/tz1090/Makefile @@ -1,5 +1,6 @@ # Makefile for TZ1090-specific clocks obj-y += clk-tz1090-deleter.o +obj-y += clk-tz1090-divider.o obj-y += clk-tz1090-gate-bank.o obj-y += clk-tz1090-mux-bank.o obj-y += clk-tz1090-pdc.o diff --git a/drivers/clk/tz1090/clk-tz1090-divider.c b/drivers/clk/tz1090/clk-tz1090-divider.c new file mode 100644 index 0000000..92788f1 --- /dev/null +++ b/drivers/clk/tz1090/clk-tz1090-divider.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@xxxxxxxxxxxxxx> + * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@xxxxxxxxxx> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@xxxxxxxxxx> + * Copyright (C) 2014 Imagination Technologies Ltd. + * + * 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. + * + * Basic clock divider in TZ1090 SoC. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#define CR_TOP_UARTCLK_DIV 0x02005928 +#define CR_TOP_META_CLKDIV 0x02005918 + +/** + * tz1090_divider_policy() - Apply policy based on the specific divider. + * @res: MMIO resource. + * @flags: Clock flags to be modified depending on the divider. + * @divider_flags: Divider flags to be modified depending on the divider. + */ +static void tz1090_divider_policy(const struct resource *res, + unsigned long *flags, u8 *divider_flags) +{ + switch (res->start) { + case CR_TOP_UARTCLK_DIV: + /* + * UART clock changes must propagate up to CLK_UART_SW, which + * muxes between XTAL1 and sys_clk_undeleted, in order to get + * enough precision. + */ + *flags |= CLK_SET_RATE_PARENT; + break; + case CR_TOP_META_CLKDIV: + /* + * The output of this divider is sys_clk_undeleted. It is set up + * by the bootloader along with the system PLL, and has a whole + * bunch of derivative peripheral clocks. It would be a really + * bad idea to allow it to change on the fly. + */ + *divider_flags |= CLK_DIVIDER_READ_ONLY; + break; + } +} + +/** + * tz1090_divider_clk_setup() - Setup function for TZ1090 divider clock. + * @node: DT node. + */ +static void tz1090_divider_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + void __iomem *reg; + const char *parent_name; + unsigned long flags = 0; + u8 divider_flags = 0; + u32 mask = 0; + u32 shift = 0; + struct resource res; + + of_property_read_string(node, "clock-output-names", &clk_name); + + parent_name = of_clk_get_parent_name(node, 0); + + reg = of_iomap(node, 0); + if (!reg) { + pr_err("%s: no memory mapped for property reg\n", __func__); + return; + } + + if (of_property_read_u32(node, "bit-mask", &mask)) { + pr_err("%s: missing bit-mask property for %s\n", + __func__, node->name); + return; + } + + /* Apply policy decisions depending on which divider this is */ + if (of_address_to_resource(node, 0, &res)) + return; + tz1090_divider_policy(&res, &flags, ÷r_flags); + + clk = clk_register_divider_mask(NULL, clk_name, parent_name, flags, reg, + shift, mask, divider_flags, NULL, NULL); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(divider_clk, "img,tz1090-divider", tz1090_divider_clk_setup); -- 2.0.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html