[PATCH 05/10] ARM: clps711x: Add CLPS711X clk driver
Alexander Shiyan
shc_work at mail.ru
Thu Jul 18 14:34:56 EDT 2013
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 "not-OF" calls implemented.
Additionally, patch removes unnecessary symbol CLKDEV_LOOKUP for CLPS711X
from main ARM Kconfig since this symbol is selected automatically by COMMON_CLK.
Signed-off-by: Alexander Shiyan <shc_work at mail.ru>
---
.../devicetree/bindings/clock/clps711x-clock.txt | 47 +++++++
arch/arm/Kconfig | 1 -
drivers/clk/Makefile | 1 +
drivers/clk/clk-clps711x.c | 150 +++++++++++++++++++++
4 files changed, 198 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/clock/clps711x-clock.txt
create mode 100644 drivers/clk/clk-clps711x.c
diff --git a/Documentation/devicetree/bindings/clock/clps711x-clock.txt b/Documentation/devicetree/bindings/clock/clps711x-clock.txt
new file mode 100644
index 0000000..0fb9fff
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/clps711x-clock.txt
@@ -0,0 +1,47 @@
+Device Tree Clock bindings for the Cirrus Logic CLPS711X CPUs
+
+=== Clock subsystem ===
+Required properties:
+- compatible: Should be "cirrus,clps711x-clk"
+- reg: Address of the internal register set
+- #clock-cells: Should be <1>
+
+The clock consumer should specify the desired clock by having the clock
+ID in its "clocks" phandle cell. The following table is a full list of
+CLPS711X clocks and IDs.
+
+ Clock ID
+ ------------------
+ dummy 0
+ cpu 1
+ pll 2
+ bus 3
+ timer_hf 4
+ timer_lf 5
+ tc1 6
+ tc2 7
+ spi 8
+ uart 9
+
+=== Clocksource subsystem ===
+Required properties:
+- compatible: Should be "cirrus,clps711x-clocksource"
+- reg: Address of the internal register set
+- clocks: phandle to the source clocks TC1 and TC2
+- interrupts: The interrupt of the TC2 timer
+
+Example:
+
+clks: clks at 80000000 {
+ compatible = "cirrus,clps711x-clk";
+ reg = <0x80000000 0>;
+ #clock-cells = <1>;
+};
+
+timer {
+ compatible = "cirrus,clps711x-clocksource";
+ reg = <0x80000000 0>;
+ clocks = <&clks 6>, <&clks 7>;
+ clock-names = "tc1", "tc2";
+ interrupts = <9>;
+};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ba412e0..dfb1e7f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -370,7 +370,6 @@ config ARCH_CLPS711X
bool "Cirrus Logic CLPS711x/EP721x/EP731x-based"
select ARCH_REQUIRE_GPIOLIB
select AUTO_ZRELADDR
- select CLKDEV_LOOKUP
select CLKSRC_MMIO
select COMMON_CLK
select CPU_ARM720T
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 4038c2b..a7e9b73 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o
# SoCs specific
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
+obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c
new file mode 100644
index 0000000..662908c
--- /dev/null
+++ b/drivers/clk/clk-clps711x.c
@@ -0,0 +1,150 @@
+/*
+ * CLPS711X clk driver
+ *
+ * Copyright (C) 2013 Alexander Shiyan <shc_work at 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.
+ */
+
+#define pr_fmt(fmt) "clk-clps711x: " fmt
+
+#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>
+
+#define CLPS711X_SYSCON1 (0x0100)
+#define CLPS711X_SYSCON2 (0x1100)
+#define CLPS711X_PLLR (0xa5a8)
+
+#define CLPS711X_EXT_FREQ (13000000)
+#define CLPS711X_OSC_FREQ (3686400)
+
+enum clps711x_clks {
+ dummy, cpu, pll, bus, timer_hf, timer_lf, tc1, tc2, spi, uart, clk_max
+};
+
+static struct {
+ struct clk_onecell_data clk_data;
+ struct clk *clks[clk_max];
+} *clps711x_clk;
+
+static void __init _clps711x_clk_init(phys_addr_t phys_base)
+{
+ const char *tc_parents[] = { "timer_lf", "timer_hf" };
+ u32 tmp, f_cpu, f_pll, f_bus, f_timh, f_spi;
+ void __iomem *pllr, *syscon1, *syscon2;
+
+ BUG_ON(!request_mem_region(phys_base + CLPS711X_PLLR, SZ_4, NULL));
+ BUG_ON(!request_mem_region(phys_base + CLPS711X_SYSCON1, SZ_128, NULL));
+ BUG_ON(!request_mem_region(phys_base + CLPS711X_SYSCON2, SZ_128, NULL));
+
+ clps711x_clk = kzalloc(sizeof(*clps711x_clk), GFP_KERNEL);
+ BUG_ON(!clps711x_clk);
+
+ pllr = ioremap(phys_base + CLPS711X_PLLR, SZ_4);
+ BUG_ON(!pllr);
+
+ syscon1 = ioremap(phys_base + CLPS711X_SYSCON1, SZ_128);
+ BUG_ON(!syscon1);
+
+ syscon2 = ioremap(phys_base + CLPS711X_SYSCON2, SZ_128);
+ BUG_ON(!syscon2);
+
+ tmp = readl(pllr) >> 24;
+ if (tmp)
+ f_pll = DIV_ROUND_UP(CLPS711X_OSC_FREQ * tmp, 2);
+ else
+ f_pll = 73728000; /* Old CPUs default value */
+
+ tmp = readl(syscon2 + SYSFLG_OFFSET);
+ if (tmp & SYSFLG2_CKMODE) {
+ f_cpu = CLPS711X_EXT_FREQ;
+ f_bus = CLPS711X_EXT_FREQ;
+ f_spi = 135400;
+ f_pll = 0;
+ } 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);
+ }
+
+ if (tmp & SYSFLG2_CKMODE) {
+ if (readl(syscon2 + SYSCON_OFFSET) & SYSCON2_OSTB)
+ f_timh = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 26);
+ else
+ f_timh = DIV_ROUND_CLOSEST(CLPS711X_EXT_FREQ, 24);
+ } else
+ f_timh = DIV_ROUND_CLOSEST(f_cpu, 144);
+
+ clps711x_clk->clks[dummy] =
+ clk_register_fixed_rate(NULL, "dummy", NULL, CLK_IS_ROOT, 0);
+ clps711x_clk->clks[cpu] =
+ clk_register_fixed_rate(NULL, "cpu", NULL, CLK_IS_ROOT, f_cpu);
+ clps711x_clk->clks[pll] =
+ clk_register_fixed_rate(NULL, "pll", NULL, CLK_IS_ROOT, f_pll);
+ clps711x_clk->clks[bus] =
+ clk_register_fixed_rate(NULL, "bus", NULL, CLK_IS_ROOT, f_bus);
+ clps711x_clk->clks[timer_hf] =
+ clk_register_fixed_rate(NULL, "timer_hf", NULL, CLK_IS_ROOT,
+ f_timh);
+ clps711x_clk->clks[timer_lf] =
+ clk_register_fixed_factor(NULL, "timer_lf", "timer_hf",
+ 0, 1, 256);
+ clps711x_clk->clks[tc1] =
+ clk_register_mux(NULL, "tc1", tc_parents, 2,
+ CLK_GET_RATE_NOCACHE, syscon1, 5, 1, 0, NULL);
+ clps711x_clk->clks[tc2] =
+ clk_register_mux(NULL, "tc2", tc_parents, 2,
+ CLK_GET_RATE_NOCACHE, syscon1, 7, 1, 0, NULL);
+ clps711x_clk->clks[spi] =
+ clk_register_fixed_rate(NULL, "spi", NULL, CLK_IS_ROOT, f_spi);
+ clps711x_clk->clks[uart] =
+ clk_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10);
+
+ clk_set_parent(clps711x_clk->clks[tc1], clps711x_clk->clks[timer_lf]);
+ clk_set_parent(clps711x_clk->clks[tc2], clps711x_clk->clks[timer_hf]);
+
+ /* Temporary export clocks for non-DT capable drivers */
+ clk_register_clkdev(clps711x_clk->clks[pll], "pll", NULL);
+ clk_register_clkdev(clps711x_clk->clks[spi], "spi", NULL);
+ clk_register_clkdev(clps711x_clk->clks[uart], "uart", NULL);
+
+ pr_debug("CPU: %u Hz, Timer: %u Hz\n", f_cpu, f_timh);
+}
+
+void __init clps711x_clk_init(phys_addr_t phys_base)
+{
+ _clps711x_clk_init(phys_base);
+
+ clk_register_clkdev(clps711x_clk->clks[tc1], "tc1", NULL);
+ clk_register_clkdev(clps711x_clk->clks[tc2], "tc2", NULL);
+}
+
+#ifdef CONFIG_OF
+static void __init clps711x_clk_init_dt(struct device_node *np)
+{
+ struct resource res;
+
+ BUG_ON(!of_address_to_resource(np, 0, &res));
+
+ _clps711x_clk_init(res.start);
+
+ 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
--
1.8.1.5
More information about the linux-arm-kernel
mailing list