[PATCH 1/2] clk: Add CLPS711X clk driver
From: Alexander Shiyan <hidden>
Date: 2014-06-01 09:54:49
Subsystem:
common clk framework, open firmware and flattened device tree bindings, the rest · Maintainers:
Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Torvalds
This adds the clock driver for Cirrus Logic CLPS711X series SoCs using common clock infrastructure. Designed primarily for migration CLPS711X subarch for multiplatform & DT, for this as the "OF" and "non-OF" calls implemented. Signed-off-by: Alexander Shiyan <redacted> --- Actual calls for driver initialization of the boards will be added later in a separate patch for the arm-soc tree to avoid possible conflicts. --- drivers/clk/Makefile | 1 + drivers/clk/clk-clps711x.c | 196 +++++++++++++++++++++++++++++ include/dt-bindings/clock/clps711x-clock.h | 27 ++++ 3 files changed, 224 insertions(+) create mode 100644 drivers/clk/clk-clps711x.c create mode 100644 include/dt-bindings/clock/clps711x-clock.h
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 4768a5d..e59a8d7 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile@@ -15,6 +15,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o +obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c
new file mode 100644
index 0000000..47685df
--- /dev/null
+++ b/drivers/clk/clk-clps711x.c@@ -0,0 +1,196 @@ +/* + * Cirrus Logic CLPS711X CLK driver + * + * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/mfd/syscon/clps711x.h> + +#include <dt-bindings/clock/clps711x-clock.h> + +#define CLPS711X_SYSCON1 (0x0100) +#define CLPS711X_SYSCON2 (0x1100) +#define CLPS711X_SYSFLG2 (0x1140) +#define CLPS711X_PLLR (0xa5a8) + +#define CLPS711X_EXT_FREQ (13000000) +#define CLPS711X_OSC_FREQ (3686400) + +static const struct clk_div_table spi_div_table[] = { + { .val = 0, .div = 32, }, + { .val = 1, .div = 8, }, + { .val = 2, .div = 2, }, + { .val = 3, .div = 1, }, +}; + +static const struct clk_div_table timer_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 256, }, +}; + +struct clps711x_clk { + struct clk_onecell_data clk_data; + spinlock_t lock; + struct clk *clks[CLPS711X_CLK_MAX]; +}; + +static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base, + u32 fref) +{ + u32 tmp, f_cpu, f_pll, f_bus, f_tim, f_pwm, f_spi; + struct clps711x_clk *clps711x_clk; + unsigned i; + + clps711x_clk = kzalloc(sizeof(*clps711x_clk), GFP_KERNEL); + if (!clps711x_clk) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&clps711x_clk->lock); + + /* Read PLL multiplier value */ + tmp = readl(base + CLPS711X_PLLR) >> 24; + if (tmp) + f_pll = DIV_ROUND_UP(CLPS711X_OSC_FREQ * tmp, 2); + else + f_pll = fref; + + tmp = readl(base + CLPS711X_SYSFLG2); + if (tmp & SYSFLG2_CKMODE) { + f_cpu = CLPS711X_EXT_FREQ; + f_bus = CLPS711X_EXT_FREQ; + f_spi = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 96); + f_pll = 0; + f_pwm = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 128); + } else { + f_cpu = f_pll; + if (f_cpu > 36864000) + f_bus = DIV_ROUND_UP(f_cpu, 2); + else + f_bus = 36864000 / 2; + f_spi = DIV_ROUND_CLOSEST(f_cpu, 576); + f_pwm = DIV_ROUND_CLOSEST(f_cpu, 768); + } + + if (tmp & SYSFLG2_CKMODE) { + if (readl(base + CLPS711X_SYSCON2) & SYSCON2_OSTB) + f_tim = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 26); + else + f_tim = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 24); + } else + f_tim = DIV_ROUND_CLOSEST(f_cpu, 144); + + tmp = readl(base + CLPS711X_SYSCON1); + /* Timer1 in free running mode. + * Counter will wrap around to 0xffff when it underflows + * and will continue to count down. + */ + tmp &= ~(SYSCON1_TC1M | SYSCON1_TC1S); + /* Timer2 in prescale mode. + * Value writen is automatically re-loaded when + * the counter underflows. + */ + tmp |= SYSCON1_TC2M | SYSCON1_TC2S; + writel(tmp, base + CLPS711X_SYSCON1); + + clps711x_clk->clks[CLPS711X_CLK_DUMMY] = + clk_register_fixed_rate(NULL, "dummy", NULL, CLK_IS_ROOT, 0); + clps711x_clk->clks[CLPS711X_CLK_CPU] = + clk_register_fixed_rate(NULL, "cpu", NULL, CLK_IS_ROOT, f_cpu); + clps711x_clk->clks[CLPS711X_CLK_BUS] = + clk_register_fixed_rate(NULL, "bus", NULL, CLK_IS_ROOT, f_bus); + clps711x_clk->clks[CLPS711X_CLK_PLL] = + clk_register_fixed_rate(NULL, "pll", NULL, CLK_IS_ROOT, f_pll); + clps711x_clk->clks[CLPS711X_CLK_TIMERREF] = + clk_register_fixed_rate(NULL, "timer_ref", NULL, CLK_IS_ROOT, + f_tim); + clps711x_clk->clks[CLPS711X_CLK_TIMER1] = + clk_register_divider_table(NULL, "timer1", "timer_ref", 0, + base + CLPS711X_SYSCON1, 5, 1, 0, + timer_div_table, &clps711x_clk->lock); + clps711x_clk->clks[CLPS711X_CLK_TIMER2] = + clk_register_divider_table(NULL, "timer2", "timer_ref", 0, + base + CLPS711X_SYSCON1, 7, 1, 0, + timer_div_table, &clps711x_clk->lock); + clps711x_clk->clks[CLPS711X_CLK_PWM] = + clk_register_fixed_rate(NULL, "pwm", NULL, CLK_IS_ROOT, f_pwm); + clps711x_clk->clks[CLPS711X_CLK_SPIREF] = + clk_register_fixed_rate(NULL, "spi_ref", NULL, CLK_IS_ROOT, + f_spi); + clps711x_clk->clks[CLPS711X_CLK_SPI] = + clk_register_divider_table(NULL, "spi", "spi_ref", 0, + base + CLPS711X_SYSCON1, 16, 2, 0, + spi_div_table, &clps711x_clk->lock); + clps711x_clk->clks[CLPS711X_CLK_UART] = + clk_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10); + clps711x_clk->clks[CLPS711X_CLK_TICK] = + clk_register_fixed_rate(NULL, "tick", NULL, CLK_IS_ROOT, 64); + + for (i = 0; i < CLPS711X_CLK_MAX; i++) + if (IS_ERR(clps711x_clk->clks[i])) + pr_err("clk %i: register failed with %ld\n", + i, PTR_ERR(clps711x_clk->clks[i])); + + return clps711x_clk; +} + +void __init clps711x_clk_init(phys_addr_t pbase, resource_size_t size) +{ + struct clps711x_clk *clps711x_clk; + void __iomem *base; + + base = ioremap(pbase, size); + BUG_ON(!base); + + clps711x_clk = _clps711x_clk_init(base, 73728000); + + BUG_ON(IS_ERR(clps711x_clk)); + + /* Clocksource */ + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_TIMER1], + NULL, "clps711x-timer.0"); + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_TIMER2], + NULL, "clps711x-timer.1"); + + /* Drivers */ + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_PWM], + NULL, "clps711x-pwm"); + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_UART], + NULL, "clps711x-uart.0"); + clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_UART], + NULL, "clps711x-uart.1"); +} + +#ifdef CONFIG_OF +static void __init clps711x_clk_init_dt(struct device_node *np) +{ + struct clps711x_clk *clps711x_clk; + u32 fref = 73728000; + void __iomem *base; + + base = of_iomap(np, 0); + BUG_ON(!base); + + WARN_ON(of_property_read_u32(np, "cpufreq", &fref)); + + clps711x_clk = _clps711x_clk_init(base, fref); + BUG_ON(IS_ERR(clps711x_clk)); + + clps711x_clk->clk_data.clks = clps711x_clk->clks; + clps711x_clk->clk_data.clk_num = clk_max; + of_clk_add_provider(np, of_clk_src_onecell_get, + &clps711x_clk->clk_data); +} +CLK_OF_DECLARE(clps711x, "cirrus,clps711x-clk", clps711x_clk_init_dt); +#endif
diff --git a/include/dt-bindings/clock/clps711x-clock.h b/include/dt-bindings/clock/clps711x-clock.h
new file mode 100644
index 0000000..0c4c80b
--- /dev/null
+++ b/include/dt-bindings/clock/clps711x-clock.h@@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> + * + * 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. + * + */ + +#ifndef __DT_BINDINGS_CLOCK_CLPS711X_H +#define __DT_BINDINGS_CLOCK_CLPS711X_H + +#define CLPS711X_CLK_DUMMY 0 +#define CLPS711X_CLK_CPU 1 +#define CLPS711X_CLK_BUS 2 +#define CLPS711X_CLK_PLL 3 +#define CLPS711X_CLK_TIMERREF 4 +#define CLPS711X_CLK_TIMER1 5 +#define CLPS711X_CLK_TIMER2 6 +#define CLPS711X_CLK_PWM 7 +#define CLPS711X_CLK_SPIREF 8 +#define CLPS711X_CLK_SPI 9 +#define CLPS711X_CLK_UART 10 +#define CLPS711X_CLK_TICK 11 +#define CLPS711X_CLK_MAX 12 + +#endif
--
1.8.5.5