[tip:timers/core] clocksource/drivers/gemini: Add driver for the Cortina Gemini

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Commit-ID:  4750535bc94b86fde06b0e698d6bac738b020be4
Gitweb:     http://git.kernel.org/tip/4750535bc94b86fde06b0e698d6bac738b020be4
Author:     Linus Walleij <linus.walleij@xxxxxxxxxx>
AuthorDate: Sun, 22 Jan 2017 13:17:17 +0100
Committer:  Daniel Lezcano <daniel.lezcano@xxxxxxxxxx>
CommitDate: Tue, 7 Feb 2017 20:58:30 +0100

clocksource/drivers/gemini: Add driver for the Cortina Gemini

This is a rewrite of the Gemini timer
driver in arch/arm/mach-gemini/timer.c trying to do everything
the device tree way:

- Make every IO-access relative to a base address and dynamic
  so we can do a dynamic ioremap and get going.
- Do not poke around directly in the global syscon registers,
  access them using the syscon regmap style design pattern for
  the one register we need to check.
- Find register range and interrupt from the device tree.

Cc: Janos Laube <janos.dev@xxxxxxxxx>
Cc: Paulius Zaleckas <paulius.zaleckas@xxxxxxxxx>
Cc: Hans Ulli Kroll <ulli.kroll@xxxxxxxxxxxxxx>
Cc: Florian Fainelli <f.fainelli@xxxxxxxxx>
Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx>
Signed-off-by: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx>
---
 drivers/clocksource/Kconfig                        |  10 ++
 drivers/clocksource/Makefile                       |   1 +
 .../time.c => drivers/clocksource/timer-gemini.c   | 168 +++++++++++++--------
 3 files changed, 114 insertions(+), 65 deletions(-)

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 3dc06f0..afef0e8 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -67,6 +67,16 @@ config DW_APB_TIMER_OF
 	select DW_APB_TIMER
 	select CLKSRC_OF
 
+config GEMINI_TIMER
+	bool "Cortina Gemini timer driver" if COMPILE_TEST
+	depends on GENERIC_CLOCKEVENTS
+	depends on HAS_IOMEM
+	select CLKSRC_MMIO
+	select CLKSRC_OF
+	select MFD_SYSCON
+	help
+	  Enables support for the Gemini timer
+
 config ROCKCHIP_TIMER
 	bool "Rockchip timer driver" if COMPILE_TEST
 	depends on ARM || ARM64
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 4b7e9a2..dbbee80 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_CLKSRC_MMIO)	+= mmio.o
 obj-$(CONFIG_DIGICOLOR_TIMER)	+= timer-digicolor.o
 obj-$(CONFIG_DW_APB_TIMER)	+= dw_apb_timer.o
 obj-$(CONFIG_DW_APB_TIMER_OF)	+= dw_apb_timer_of.o
+obj-$(CONFIG_GEMINI_TIMER)	+= timer-gemini.o
 obj-$(CONFIG_ROCKCHIP_TIMER)      += rockchip_timer.o
 obj-$(CONFIG_CLKSRC_NOMADIK_MTU)	+= nomadik-mtu.o
 obj-$(CONFIG_CLKSRC_DBX500_PRCMU)	+= clksrc-dbx500-prcmu.o
diff --git a/arch/arm/mach-gemini/time.c b/drivers/clocksource/timer-gemini.c
similarity index 55%
copy from arch/arm/mach-gemini/time.c
copy to drivers/clocksource/timer-gemini.c
index f5f18df..dda27b7 100644
--- a/arch/arm/mach-gemini/time.c
+++ b/drivers/clocksource/timer-gemini.c
@@ -1,37 +1,51 @@
 /*
- *  Copyright (C) 2001-2006 Storlink, Corp.
- *  Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@xxxxxxxxxxxx>
+ * Gemini timer driver
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@xxxxxxxxxx>
  *
- * 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.
+ * Based on a rewrite of arch/arm/mach-gemini/timer.c:
+ * Copyright (C) 2001-2006 Storlink, Corp.
+ * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@xxxxxxxxxxxx>
  */
 #include <linux/interrupt.h>
-#include <linux/irq.h>
 #include <linux/io.h>
-#include <mach/hardware.h>
-#include <mach/global_reg.h>
-#include <asm/mach/time.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 #include <linux/clockchips.h>
 #include <linux/clocksource.h>
 #include <linux/sched_clock.h>
 
 /*
- * Register definitions for the timers
+ * Relevant registers in the global syscon
  */
+#define GLOBAL_STATUS		0x04
+#define CPU_AHB_RATIO_MASK	(0x3 << 18)
+#define CPU_AHB_1_1		(0x0 << 18)
+#define CPU_AHB_3_2		(0x1 << 18)
+#define CPU_AHB_24_13		(0x2 << 18)
+#define CPU_AHB_2_1		(0x3 << 18)
+#define REG_TO_AHB_SPEED(reg)	((((reg) >> 15) & 0x7) * 10 + 130)
 
-#define TIMER1_BASE		GEMINI_TIMER_BASE
-#define TIMER2_BASE		(GEMINI_TIMER_BASE + 0x10)
-#define TIMER3_BASE		(GEMINI_TIMER_BASE + 0x20)
-
-#define TIMER_COUNT(BASE)	(IO_ADDRESS(BASE) + 0x00)
-#define TIMER_LOAD(BASE)	(IO_ADDRESS(BASE) + 0x04)
-#define TIMER_MATCH1(BASE)	(IO_ADDRESS(BASE) + 0x08)
-#define TIMER_MATCH2(BASE)	(IO_ADDRESS(BASE) + 0x0C)
-#define TIMER_CR		(IO_ADDRESS(GEMINI_TIMER_BASE) + 0x30)
-#define TIMER_INTR_STATE	(IO_ADDRESS(GEMINI_TIMER_BASE) + 0x34)
-#define TIMER_INTR_MASK		(IO_ADDRESS(GEMINI_TIMER_BASE) + 0x38)
+/*
+ * Register definitions for the timers
+ */
+#define TIMER1_COUNT		(0x00)
+#define TIMER1_LOAD		(0x04)
+#define TIMER1_MATCH1		(0x08)
+#define TIMER1_MATCH2		(0x0c)
+#define TIMER2_COUNT		(0x10)
+#define TIMER2_LOAD		(0x14)
+#define TIMER2_MATCH1		(0x18)
+#define TIMER2_MATCH2		(0x1c)
+#define TIMER3_COUNT		(0x20)
+#define TIMER3_LOAD		(0x24)
+#define TIMER3_MATCH1		(0x28)
+#define TIMER3_MATCH2		(0x2c)
+#define TIMER_CR		(0x30)
+#define TIMER_INTR_STATE	(0x34)
+#define TIMER_INTR_MASK		(0x38)
 
 #define TIMER_1_CR_ENABLE	(1 << 0)
 #define TIMER_1_CR_CLOCK	(1 << 1)
@@ -60,12 +74,12 @@
 #define TIMER_3_INT_OVERFLOW	(1 << 8)
 #define TIMER_INT_ALL_MASK	0x1ff
 
-
 static unsigned int tick_rate;
+static void __iomem *base;
 
 static u64 notrace gemini_read_sched_clock(void)
 {
-	return readl(TIMER_COUNT(TIMER3_BASE));
+	return readl(base + TIMER3_COUNT);
 }
 
 static int gemini_timer_set_next_event(unsigned long cycles,
@@ -74,9 +88,9 @@ static int gemini_timer_set_next_event(unsigned long cycles,
 	u32 cr;
 
 	/* Setup the match register */
-	cr = readl(TIMER_COUNT(TIMER1_BASE));
-	writel(cr + cycles, TIMER_MATCH1(TIMER1_BASE));
-	if (readl(TIMER_COUNT(TIMER1_BASE)) - cr > cycles)
+	cr = readl(base + TIMER1_COUNT);
+	writel(cr + cycles, base + TIMER1_MATCH1);
+	if (readl(base + TIMER1_COUNT) - cr > cycles)
 		return -ETIME;
 
 	return 0;
@@ -91,24 +105,24 @@ static int gemini_timer_shutdown(struct clock_event_device *evt)
 	 * instead.
 	 */
 	/* Stop timer and interrupt. */
-	cr = readl(TIMER_CR);
+	cr = readl(base + TIMER_CR);
 	cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
-	writel(cr, TIMER_CR);
+	writel(cr, base + TIMER_CR);
 
 	/* Setup counter start from 0 */
-	writel(0, TIMER_COUNT(TIMER1_BASE));
-	writel(0, TIMER_LOAD(TIMER1_BASE));
+	writel(0, base + TIMER1_COUNT);
+	writel(0, base + TIMER1_LOAD);
 
 	/* enable interrupt */
-	cr = readl(TIMER_INTR_MASK);
+	cr = readl(base + TIMER_INTR_MASK);
 	cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2);
 	cr |= TIMER_1_INT_MATCH1;
-	writel(cr, TIMER_INTR_MASK);
+	writel(cr, base + TIMER_INTR_MASK);
 
 	/* start the timer */
-	cr = readl(TIMER_CR);
+	cr = readl(base + TIMER_CR);
 	cr |= TIMER_1_CR_ENABLE;
-	writel(cr, TIMER_CR);
+	writel(cr, base + TIMER_CR);
 
 	return 0;
 }
@@ -119,26 +133,26 @@ static int gemini_timer_set_periodic(struct clock_event_device *evt)
 	u32 cr;
 
 	/* Stop timer and interrupt */
-	cr = readl(TIMER_CR);
+	cr = readl(base + TIMER_CR);
 	cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT);
-	writel(cr, TIMER_CR);
+	writel(cr, base + TIMER_CR);
 
 	/* Setup timer to fire at 1/HT intervals. */
 	cr = 0xffffffff - (period - 1);
-	writel(cr, TIMER_COUNT(TIMER1_BASE));
-	writel(cr, TIMER_LOAD(TIMER1_BASE));
+	writel(cr, base + TIMER1_COUNT);
+	writel(cr, base + TIMER1_LOAD);
 
 	/* enable interrupt on overflow */
-	cr = readl(TIMER_INTR_MASK);
+	cr = readl(base + TIMER_INTR_MASK);
 	cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2);
 	cr |= TIMER_1_INT_OVERFLOW;
-	writel(cr, TIMER_INTR_MASK);
+	writel(cr, base + TIMER_INTR_MASK);
 
 	/* Start the timer */
-	cr = readl(TIMER_CR);
+	cr = readl(base + TIMER_CR);
 	cr |= TIMER_1_CR_ENABLE;
 	cr |= TIMER_1_CR_INT;
-	writel(cr, TIMER_CR);
+	writel(cr, base + TIMER_CR);
 
 	return 0;
 }
@@ -175,21 +189,42 @@ static struct irqaction gemini_timer_irq = {
 	.handler	= gemini_timer_interrupt,
 };
 
-/*
- * Set up timer interrupt, and return the current time in seconds.
- */
-void __init gemini_timer_init(void)
+static int __init gemini_timer_of_init(struct device_node *np)
 {
-	u32 reg_v;
+	static struct regmap *map;
+	int irq;
+	int ret;
+	u32 val;
+
+	map = syscon_regmap_lookup_by_phandle(np, "syscon");
+	if (IS_ERR(map)) {
+		pr_err("Can't get regmap for syscon handle");
+		return -ENODEV;
+	}
+	ret = regmap_read(map, GLOBAL_STATUS, &val);
+	if (ret) {
+		pr_err("Can't read syscon status register");
+		return -ENXIO;
+	}
 
-	reg_v = readl(IO_ADDRESS(GEMINI_GLOBAL_BASE + GLOBAL_STATUS));
-	tick_rate = REG_TO_AHB_SPEED(reg_v) * 1000000;
+	base = of_iomap(np, 0);
+	if (!base) {
+		pr_err("Can't remap registers");
+		return -ENXIO;
+	}
+	/* IRQ for timer 1 */
+	irq = irq_of_parse_and_map(np, 0);
+	if (irq <= 0) {
+		pr_err("Can't parse IRQ");
+		return -EINVAL;
+	}
 
+	tick_rate = REG_TO_AHB_SPEED(val) * 1000000;
 	printk(KERN_INFO "Bus: %dMHz", tick_rate / 1000000);
 
 	tick_rate /= 6;		/* APB bus run AHB*(1/6) */
 
-	switch(reg_v & CPU_AHB_RATIO_MASK) {
+	switch (val & CPU_AHB_RATIO_MASK) {
 	case CPU_AHB_1_1:
 		printk(KERN_CONT "(1/1)\n");
 		break;
@@ -207,33 +242,36 @@ void __init gemini_timer_init(void)
 	/*
 	 * Reset the interrupt mask and status
 	 */
-	writel(TIMER_INT_ALL_MASK, TIMER_INTR_MASK);
-	writel(0, TIMER_INTR_STATE);
-	writel(TIMER_DEFAULT_FLAGS, TIMER_CR);
+	writel(TIMER_INT_ALL_MASK, base + TIMER_INTR_MASK);
+	writel(0, base + TIMER_INTR_STATE);
+	writel(TIMER_DEFAULT_FLAGS, base + TIMER_CR);
 
 	/*
 	 * Setup free-running clocksource timer (interrupts
 	 * disabled.)
 	 */
-	writel(0, TIMER_COUNT(TIMER3_BASE));
-	writel(0, TIMER_LOAD(TIMER3_BASE));
-	writel(0, TIMER_MATCH1(TIMER3_BASE));
-	writel(0, TIMER_MATCH2(TIMER3_BASE));
-	clocksource_mmio_init(TIMER_COUNT(TIMER3_BASE),
+	writel(0, base + TIMER3_COUNT);
+	writel(0, base + TIMER3_LOAD);
+	writel(0, base + TIMER3_MATCH1);
+	writel(0, base + TIMER3_MATCH2);
+	clocksource_mmio_init(base + TIMER3_COUNT,
 			      "gemini_clocksource", tick_rate,
 			      300, 32, clocksource_mmio_readl_up);
 	sched_clock_register(gemini_read_sched_clock, 32, tick_rate);
 
 	/*
 	 * Setup clockevent timer (interrupt-driven.)
-	*/
-	writel(0, TIMER_COUNT(TIMER1_BASE));
-	writel(0, TIMER_LOAD(TIMER1_BASE));
-	writel(0, TIMER_MATCH1(TIMER1_BASE));
-	writel(0, TIMER_MATCH2(TIMER1_BASE));
-	setup_irq(IRQ_TIMER1, &gemini_timer_irq);
+	 */
+	writel(0, base + TIMER1_COUNT);
+	writel(0, base + TIMER1_LOAD);
+	writel(0, base + TIMER1_MATCH1);
+	writel(0, base + TIMER1_MATCH2);
+	setup_irq(irq, &gemini_timer_irq);
 	gemini_clockevent.cpumask = cpumask_of(0);
 	clockevents_config_and_register(&gemini_clockevent, tick_rate,
 					1, 0xffffffff);
 
+	return 0;
 }
+CLOCKSOURCE_OF_DECLARE(nomadik_mtu, "cortina,gemini-timer",
+		       gemini_timer_of_init);
--
To unsubscribe from this list: send the line "unsubscribe linux-tip-commits" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Stable Commits]     [Linux Stable Kernel]     [Linux Kernel]     [Linux USB Devel]     [Linux Video &Media]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux