ARM: 8366/1: move Dual-Timer SP804 driver to drivers/clocksource
authorSudeep Holla <sudeep.holla@arm.com>
Mon, 18 May 2015 15:29:40 +0000 (16:29 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Tue, 2 Jun 2015 08:58:18 +0000 (09:58 +0100)
The ARM Dual-Timer SP804 module is peripheral found not only on ARM32
platforms but also on ARM64 platforms.

This patch moves the driver out of arch/arm to driver/clocksource
so that it can be used on ARM64 platforms also.

Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Rob Herring <robh@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Olof Johansson <olof@lixom.net>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
14 files changed:
arch/arm/Kconfig
arch/arm/common/Makefile
arch/arm/common/timer-sp.c [deleted file]
arch/arm/include/asm/hardware/arm_timer.h [deleted file]
arch/arm/include/asm/hardware/timer-sp.h [deleted file]
arch/arm/mach-nspire/nspire.c
arch/arm/mach-realview/core.c
arch/arm/mach-versatile/core.c
drivers/clocksource/Kconfig
drivers/clocksource/Makefile
drivers/clocksource/timer-integrator-ap.c
drivers/clocksource/timer-sp.h [new file with mode: 0644]
drivers/clocksource/timer-sp804.c [new file with mode: 0644]
include/clocksource/timer-sp804.h [new file with mode: 0644]

index d0950ce..34b7285 100644 (file)
@@ -975,11 +975,6 @@ config PLAT_PXA
 config PLAT_VERSATILE
        bool
 
-config ARM_TIMER_SP804
-       bool
-       select CLKSRC_MMIO
-       select CLKSRC_OF if OF
-
 source "arch/arm/firmware/Kconfig"
 
 source arch/arm/mm/Kconfig
index 70b1eff..6ee5959 100644 (file)
@@ -11,7 +11,6 @@ obj-$(CONFIG_SHARP_LOCOMO)    += locomo.o
 obj-$(CONFIG_SHARP_PARAM)      += sharpsl_param.o
 obj-$(CONFIG_SHARP_SCOOP)      += scoop.o
 obj-$(CONFIG_PCI_HOST_ITE8152)  += it8152.o
-obj-$(CONFIG_ARM_TIMER_SP804)  += timer-sp.o
 obj-$(CONFIG_MCPM)             += mcpm_head.o mcpm_entry.o mcpm_platsmp.o vlock.o
 CFLAGS_REMOVE_mcpm_entry.o     = -pg
 AFLAGS_mcpm_head.o             := -march=armv7-a
diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c
deleted file mode 100644 (file)
index 000aea3..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- *  linux/arch/arm/common/timer-sp.c
- *
- *  Copyright (C) 1999 - 2003 ARM Limited
- *  Copyright (C) 2000 Deep Blue Solutions Ltd
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-#include <linux/clk.h>
-#include <linux/clocksource.h>
-#include <linux/clockchips.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/sched_clock.h>
-
-#include <asm/hardware/arm_timer.h>
-#include <asm/hardware/timer-sp.h>
-
-static long __init sp804_get_clock_rate(struct clk *clk)
-{
-       long rate;
-       int err;
-
-       err = clk_prepare(clk);
-       if (err) {
-               pr_err("sp804: clock failed to prepare: %d\n", err);
-               clk_put(clk);
-               return err;
-       }
-
-       err = clk_enable(clk);
-       if (err) {
-               pr_err("sp804: clock failed to enable: %d\n", err);
-               clk_unprepare(clk);
-               clk_put(clk);
-               return err;
-       }
-
-       rate = clk_get_rate(clk);
-       if (rate < 0) {
-               pr_err("sp804: clock failed to get rate: %ld\n", rate);
-               clk_disable(clk);
-               clk_unprepare(clk);
-               clk_put(clk);
-       }
-
-       return rate;
-}
-
-static void __iomem *sched_clock_base;
-
-static u64 notrace sp804_read(void)
-{
-       return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
-}
-
-void __init sp804_timer_disable(void __iomem *base)
-{
-       writel(0, base + TIMER_CTRL);
-}
-
-void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
-                                                    const char *name,
-                                                    struct clk *clk,
-                                                    int use_sched_clock)
-{
-       long rate;
-
-       if (!clk) {
-               clk = clk_get_sys("sp804", name);
-               if (IS_ERR(clk)) {
-                       pr_err("sp804: clock not found: %d\n",
-                              (int)PTR_ERR(clk));
-                       return;
-               }
-       }
-
-       rate = sp804_get_clock_rate(clk);
-
-       if (rate < 0)
-               return;
-
-       /* setup timer 0 as free-running clocksource */
-       writel(0, base + TIMER_CTRL);
-       writel(0xffffffff, base + TIMER_LOAD);
-       writel(0xffffffff, base + TIMER_VALUE);
-       writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
-               base + TIMER_CTRL);
-
-       clocksource_mmio_init(base + TIMER_VALUE, name,
-               rate, 200, 32, clocksource_mmio_readl_down);
-
-       if (use_sched_clock) {
-               sched_clock_base = base;
-               sched_clock_register(sp804_read, 32, rate);
-       }
-}
-
-
-static void __iomem *clkevt_base;
-static unsigned long clkevt_reload;
-
-/*
- * IRQ handler for the timer
- */
-static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id)
-{
-       struct clock_event_device *evt = dev_id;
-
-       /* clear the interrupt */
-       writel(1, clkevt_base + TIMER_INTCLR);
-
-       evt->event_handler(evt);
-
-       return IRQ_HANDLED;
-}
-
-static void sp804_set_mode(enum clock_event_mode mode,
-       struct clock_event_device *evt)
-{
-       unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE;
-
-       writel(ctrl, clkevt_base + TIMER_CTRL);
-
-       switch (mode) {
-       case CLOCK_EVT_MODE_PERIODIC:
-               writel(clkevt_reload, clkevt_base + TIMER_LOAD);
-               ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
-               break;
-
-       case CLOCK_EVT_MODE_ONESHOT:
-               /* period set, and timer enabled in 'next_event' hook */
-               ctrl |= TIMER_CTRL_ONESHOT;
-               break;
-
-       case CLOCK_EVT_MODE_UNUSED:
-       case CLOCK_EVT_MODE_SHUTDOWN:
-       default:
-               break;
-       }
-
-       writel(ctrl, clkevt_base + TIMER_CTRL);
-}
-
-static int sp804_set_next_event(unsigned long next,
-       struct clock_event_device *evt)
-{
-       unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
-
-       writel(next, clkevt_base + TIMER_LOAD);
-       writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
-
-       return 0;
-}
-
-static struct clock_event_device sp804_clockevent = {
-       .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
-               CLOCK_EVT_FEAT_DYNIRQ,
-       .set_mode       = sp804_set_mode,
-       .set_next_event = sp804_set_next_event,
-       .rating         = 300,
-};
-
-static struct irqaction sp804_timer_irq = {
-       .name           = "timer",
-       .flags          = IRQF_TIMER | IRQF_IRQPOLL,
-       .handler        = sp804_timer_interrupt,
-       .dev_id         = &sp804_clockevent,
-};
-
-void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name)
-{
-       struct clock_event_device *evt = &sp804_clockevent;
-       long rate;
-
-       if (!clk)
-               clk = clk_get_sys("sp804", name);
-       if (IS_ERR(clk)) {
-               pr_err("sp804: %s clock not found: %d\n", name,
-                       (int)PTR_ERR(clk));
-               return;
-       }
-
-       rate = sp804_get_clock_rate(clk);
-       if (rate < 0)
-               return;
-
-       clkevt_base = base;
-       clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ);
-       evt->name = name;
-       evt->irq = irq;
-       evt->cpumask = cpu_possible_mask;
-
-       writel(0, base + TIMER_CTRL);
-
-       setup_irq(irq, &sp804_timer_irq);
-       clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
-}
-
-static void __init sp804_of_init(struct device_node *np)
-{
-       static bool initialized = false;
-       void __iomem *base;
-       int irq;
-       u32 irq_num = 0;
-       struct clk *clk1, *clk2;
-       const char *name = of_get_property(np, "compatible", NULL);
-
-       base = of_iomap(np, 0);
-       if (WARN_ON(!base))
-               return;
-
-       /* Ensure timers are disabled */
-       writel(0, base + TIMER_CTRL);
-       writel(0, base + TIMER_2_BASE + TIMER_CTRL);
-
-       if (initialized || !of_device_is_available(np))
-               goto err;
-
-       clk1 = of_clk_get(np, 0);
-       if (IS_ERR(clk1))
-               clk1 = NULL;
-
-       /* Get the 2nd clock if the timer has 3 timer clocks */
-       if (of_count_phandle_with_args(np, "clocks", "#clock-cells") == 3) {
-               clk2 = of_clk_get(np, 1);
-               if (IS_ERR(clk2)) {
-                       pr_err("sp804: %s clock not found: %d\n", np->name,
-                               (int)PTR_ERR(clk2));
-                       clk2 = NULL;
-               }
-       } else
-               clk2 = clk1;
-
-       irq = irq_of_parse_and_map(np, 0);
-       if (irq <= 0)
-               goto err;
-
-       of_property_read_u32(np, "arm,sp804-has-irq", &irq_num);
-       if (irq_num == 2) {
-               __sp804_clockevents_init(base + TIMER_2_BASE, irq, clk2, name);
-               __sp804_clocksource_and_sched_clock_init(base, name, clk1, 1);
-       } else {
-               __sp804_clockevents_init(base, irq, clk1 , name);
-               __sp804_clocksource_and_sched_clock_init(base + TIMER_2_BASE,
-                                                        name, clk2, 1);
-       }
-       initialized = true;
-
-       return;
-err:
-       iounmap(base);
-}
-CLOCKSOURCE_OF_DECLARE(sp804, "arm,sp804", sp804_of_init);
-
-static void __init integrator_cp_of_init(struct device_node *np)
-{
-       static int init_count = 0;
-       void __iomem *base;
-       int irq;
-       const char *name = of_get_property(np, "compatible", NULL);
-       struct clk *clk;
-
-       base = of_iomap(np, 0);
-       if (WARN_ON(!base))
-               return;
-       clk = of_clk_get(np, 0);
-       if (WARN_ON(IS_ERR(clk)))
-               return;
-
-       /* Ensure timer is disabled */
-       writel(0, base + TIMER_CTRL);
-
-       if (init_count == 2 || !of_device_is_available(np))
-               goto err;
-
-       if (!init_count)
-               __sp804_clocksource_and_sched_clock_init(base, name, clk, 0);
-       else {
-               irq = irq_of_parse_and_map(np, 0);
-               if (irq <= 0)
-                       goto err;
-
-               __sp804_clockevents_init(base, irq, clk, name);
-       }
-
-       init_count++;
-       return;
-err:
-       iounmap(base);
-}
-CLOCKSOURCE_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
diff --git a/arch/arm/include/asm/hardware/arm_timer.h b/arch/arm/include/asm/hardware/arm_timer.h
deleted file mode 100644 (file)
index d6030ff..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef __ASM_ARM_HARDWARE_ARM_TIMER_H
-#define __ASM_ARM_HARDWARE_ARM_TIMER_H
-
-/*
- * ARM timer implementation, found in Integrator, Versatile and Realview
- * platforms.  Not all platforms support all registers and bits in these
- * registers, so we mark them with A for Integrator AP, C for Integrator
- * CP, V for Versatile and R for Realview.
- *
- * Integrator AP has 16-bit timers, Integrator CP, Versatile and Realview
- * can have 16-bit or 32-bit selectable via a bit in the control register.
- *
- * Every SP804 contains two identical timers.
- */
-#define TIMER_1_BASE   0x00
-#define TIMER_2_BASE   0x20
-
-#define TIMER_LOAD     0x00                    /* ACVR rw */
-#define TIMER_VALUE    0x04                    /* ACVR ro */
-#define TIMER_CTRL     0x08                    /* ACVR rw */
-#define TIMER_CTRL_ONESHOT     (1 << 0)        /*  CVR */
-#define TIMER_CTRL_32BIT       (1 << 1)        /*  CVR */
-#define TIMER_CTRL_DIV1                (0 << 2)        /* ACVR */
-#define TIMER_CTRL_DIV16       (1 << 2)        /* ACVR */
-#define TIMER_CTRL_DIV256      (2 << 2)        /* ACVR */
-#define TIMER_CTRL_IE          (1 << 5)        /*   VR */
-#define TIMER_CTRL_PERIODIC    (1 << 6)        /* ACVR */
-#define TIMER_CTRL_ENABLE      (1 << 7)        /* ACVR */
-
-#define TIMER_INTCLR   0x0c                    /* ACVR wo */
-#define TIMER_RIS      0x10                    /*  CVR ro */
-#define TIMER_MIS      0x14                    /*  CVR ro */
-#define TIMER_BGLOAD   0x18                    /*  CVR rw */
-
-#endif
diff --git a/arch/arm/include/asm/hardware/timer-sp.h b/arch/arm/include/asm/hardware/timer-sp.h
deleted file mode 100644 (file)
index 05eaefa..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-struct clk;
-
-void __sp804_clocksource_and_sched_clock_init(void __iomem *,
-                                             const char *, struct clk *, int);
-void __sp804_clockevents_init(void __iomem *, unsigned int,
-                             struct clk *, const char *);
-void sp804_timer_disable(void __iomem *);
-
-static inline void sp804_clocksource_init(void __iomem *base, const char *name)
-{
-       __sp804_clocksource_and_sched_clock_init(base, name, NULL, 0);
-}
-
-static inline void sp804_clocksource_and_sched_clock_init(void __iomem *base,
-                                                         const char *name)
-{
-       __sp804_clocksource_and_sched_clock_init(base, name, NULL, 1);
-}
-
-static inline void sp804_clockevents_init(void __iomem *base, unsigned int irq, const char *name)
-{
-       __sp804_clockevents_init(base, irq, NULL, name);
-
-}
index 3445a56..34c2a1b 100644 (file)
@@ -22,8 +22,6 @@
 #include <asm/mach-types.h>
 #include <asm/mach/map.h>
 
-#include <asm/hardware/timer-sp.h>
-
 #include "mmio.h"
 #include "clcd.h"
 
index c611f48..44575ed 100644 (file)
@@ -35,6 +35,8 @@
 #include <linux/mtd/physmap.h>
 #include <linux/memblock.h>
 
+#include <clocksource/timer-sp804.h>
+
 #include <mach/hardware.h>
 #include <asm/irq.h>
 #include <asm/mach-types.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/map.h>
 
-
 #include <mach/platform.h>
 #include <mach/irqs.h>
-#include <asm/hardware/timer-sp.h>
 
 #include <plat/sched_clock.h>
 
index f98c196..23a04fe 100644 (file)
@@ -41,6 +41,8 @@
 #include <linux/bitops.h>
 #include <linux/reboot.h>
 
+#include <clocksource/timer-sp804.h>
+
 #include <asm/irq.h>
 #include <asm/hardware/icst.h>
 #include <asm/mach-types.h>
@@ -51,7 +53,6 @@
 #include <asm/mach/map.h>
 #include <mach/hardware.h>
 #include <mach/platform.h>
-#include <asm/hardware/timer-sp.h>
 
 #include <plat/sched_clock.h>
 
index 51d7865..0f1c0e7 100644 (file)
@@ -132,6 +132,11 @@ config ARM_GLOBAL_TIMER
        help
          This options enables support for the ARM global timer unit
 
+config ARM_TIMER_SP804
+       bool "Support for Dual Timer SP804 module"
+       select CLKSRC_MMIO
+       select CLKSRC_OF if OF
+
 config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
        bool
        depends on ARM_GLOBAL_TIMER
index 5b85f6a..5ca59f9 100644 (file)
@@ -45,6 +45,7 @@ obj-$(CONFIG_MTK_TIMER)               += mtk_timer.o
 
 obj-$(CONFIG_ARM_ARCH_TIMER)           += arm_arch_timer.o
 obj-$(CONFIG_ARM_GLOBAL_TIMER)         += arm_global_timer.o
+obj-$(CONFIG_ARM_TIMER_SP804)          += timer-sp804.o
 obj-$(CONFIG_CLKSRC_METAG_GENERIC)     += metag_generic.o
 obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST)  += dummy_timer.o
 obj-$(CONFIG_ARCH_KEYSTONE)            += timer-keystone.o
index b9efd30..d7d21e4 100644 (file)
@@ -26,7 +26,8 @@
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
 #include <linux/sched_clock.h>
-#include <asm/hardware/arm_timer.h>
+
+#include "timer-sp.h"
 
 static void __iomem * sched_clk_base;
 
diff --git a/drivers/clocksource/timer-sp.h b/drivers/clocksource/timer-sp.h
new file mode 100644 (file)
index 0000000..050d885
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * ARM timer implementation, found in Integrator, Versatile and Realview
+ * platforms.  Not all platforms support all registers and bits in these
+ * registers, so we mark them with A for Integrator AP, C for Integrator
+ * CP, V for Versatile and R for Realview.
+ *
+ * Integrator AP has 16-bit timers, Integrator CP, Versatile and Realview
+ * can have 16-bit or 32-bit selectable via a bit in the control register.
+ *
+ * Every SP804 contains two identical timers.
+ */
+#define TIMER_1_BASE   0x00
+#define TIMER_2_BASE   0x20
+
+#define TIMER_LOAD     0x00                    /* ACVR rw */
+#define TIMER_VALUE    0x04                    /* ACVR ro */
+#define TIMER_CTRL     0x08                    /* ACVR rw */
+#define TIMER_CTRL_ONESHOT     (1 << 0)        /*  CVR */
+#define TIMER_CTRL_32BIT       (1 << 1)        /*  CVR */
+#define TIMER_CTRL_DIV1                (0 << 2)        /* ACVR */
+#define TIMER_CTRL_DIV16       (1 << 2)        /* ACVR */
+#define TIMER_CTRL_DIV256      (2 << 2)        /* ACVR */
+#define TIMER_CTRL_IE          (1 << 5)        /*   VR */
+#define TIMER_CTRL_PERIODIC    (1 << 6)        /* ACVR */
+#define TIMER_CTRL_ENABLE      (1 << 7)        /* ACVR */
+
+#define TIMER_INTCLR   0x0c                    /* ACVR wo */
+#define TIMER_RIS      0x10                    /*  CVR ro */
+#define TIMER_MIS      0x14                    /*  CVR ro */
+#define TIMER_BGLOAD   0x18                    /*  CVR rw */
diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c
new file mode 100644 (file)
index 0000000..ca02503
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ *  linux/drivers/clocksource/timer-sp.c
+ *
+ *  Copyright (C) 1999 - 2003 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+
+#include <clocksource/timer-sp804.h>
+
+#include "timer-sp.h"
+
+static long __init sp804_get_clock_rate(struct clk *clk)
+{
+       long rate;
+       int err;
+
+       err = clk_prepare(clk);
+       if (err) {
+               pr_err("sp804: clock failed to prepare: %d\n", err);
+               clk_put(clk);
+               return err;
+       }
+
+       err = clk_enable(clk);
+       if (err) {
+               pr_err("sp804: clock failed to enable: %d\n", err);
+               clk_unprepare(clk);
+               clk_put(clk);
+               return err;
+       }
+
+       rate = clk_get_rate(clk);
+       if (rate < 0) {
+               pr_err("sp804: clock failed to get rate: %ld\n", rate);
+               clk_disable(clk);
+               clk_unprepare(clk);
+               clk_put(clk);
+       }
+
+       return rate;
+}
+
+static void __iomem *sched_clock_base;
+
+static u64 notrace sp804_read(void)
+{
+       return ~readl_relaxed(sched_clock_base + TIMER_VALUE);
+}
+
+void __init sp804_timer_disable(void __iomem *base)
+{
+       writel(0, base + TIMER_CTRL);
+}
+
+void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
+                                                    const char *name,
+                                                    struct clk *clk,
+                                                    int use_sched_clock)
+{
+       long rate;
+
+       if (!clk) {
+               clk = clk_get_sys("sp804", name);
+               if (IS_ERR(clk)) {
+                       pr_err("sp804: clock not found: %d\n",
+                              (int)PTR_ERR(clk));
+                       return;
+               }
+       }
+
+       rate = sp804_get_clock_rate(clk);
+
+       if (rate < 0)
+               return;
+
+       /* setup timer 0 as free-running clocksource */
+       writel(0, base + TIMER_CTRL);
+       writel(0xffffffff, base + TIMER_LOAD);
+       writel(0xffffffff, base + TIMER_VALUE);
+       writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
+               base + TIMER_CTRL);
+
+       clocksource_mmio_init(base + TIMER_VALUE, name,
+               rate, 200, 32, clocksource_mmio_readl_down);
+
+       if (use_sched_clock) {
+               sched_clock_base = base;
+               sched_clock_register(sp804_read, 32, rate);
+       }
+}
+
+
+static void __iomem *clkevt_base;
+static unsigned long clkevt_reload;
+
+/*
+ * IRQ handler for the timer
+ */
+static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id)
+{
+       struct clock_event_device *evt = dev_id;
+
+       /* clear the interrupt */
+       writel(1, clkevt_base + TIMER_INTCLR);
+
+       evt->event_handler(evt);
+
+       return IRQ_HANDLED;
+}
+
+static void sp804_set_mode(enum clock_event_mode mode,
+       struct clock_event_device *evt)
+{
+       unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE;
+
+       writel(ctrl, clkevt_base + TIMER_CTRL);
+
+       switch (mode) {
+       case CLOCK_EVT_MODE_PERIODIC:
+               writel(clkevt_reload, clkevt_base + TIMER_LOAD);
+               ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
+               break;
+
+       case CLOCK_EVT_MODE_ONESHOT:
+               /* period set, and timer enabled in 'next_event' hook */
+               ctrl |= TIMER_CTRL_ONESHOT;
+               break;
+
+       case CLOCK_EVT_MODE_UNUSED:
+       case CLOCK_EVT_MODE_SHUTDOWN:
+       default:
+               break;
+       }
+
+       writel(ctrl, clkevt_base + TIMER_CTRL);
+}
+
+static int sp804_set_next_event(unsigned long next,
+       struct clock_event_device *evt)
+{
+       unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
+
+       writel(next, clkevt_base + TIMER_LOAD);
+       writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
+
+       return 0;
+}
+
+static struct clock_event_device sp804_clockevent = {
+       .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
+               CLOCK_EVT_FEAT_DYNIRQ,
+       .set_mode       = sp804_set_mode,
+       .set_next_event = sp804_set_next_event,
+       .rating         = 300,
+};
+
+static struct irqaction sp804_timer_irq = {
+       .name           = "timer",
+       .flags          = IRQF_TIMER | IRQF_IRQPOLL,
+       .handler        = sp804_timer_interrupt,
+       .dev_id         = &sp804_clockevent,
+};
+
+void __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name)
+{
+       struct clock_event_device *evt = &sp804_clockevent;
+       long rate;
+
+       if (!clk)
+               clk = clk_get_sys("sp804", name);
+       if (IS_ERR(clk)) {
+               pr_err("sp804: %s clock not found: %d\n", name,
+                       (int)PTR_ERR(clk));
+               return;
+       }
+
+       rate = sp804_get_clock_rate(clk);
+       if (rate < 0)
+               return;
+
+       clkevt_base = base;
+       clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ);
+       evt->name = name;
+       evt->irq = irq;
+       evt->cpumask = cpu_possible_mask;
+
+       writel(0, base + TIMER_CTRL);
+
+       setup_irq(irq, &sp804_timer_irq);
+       clockevents_config_and_register(evt, rate, 0xf, 0xffffffff);
+}
+
+static void __init sp804_of_init(struct device_node *np)
+{
+       static bool initialized = false;
+       void __iomem *base;
+       int irq;
+       u32 irq_num = 0;
+       struct clk *clk1, *clk2;
+       const char *name = of_get_property(np, "compatible", NULL);
+
+       base = of_iomap(np, 0);
+       if (WARN_ON(!base))
+               return;
+
+       /* Ensure timers are disabled */
+       writel(0, base + TIMER_CTRL);
+       writel(0, base + TIMER_2_BASE + TIMER_CTRL);
+
+       if (initialized || !of_device_is_available(np))
+               goto err;
+
+       clk1 = of_clk_get(np, 0);
+       if (IS_ERR(clk1))
+               clk1 = NULL;
+
+       /* Get the 2nd clock if the timer has 3 timer clocks */
+       if (of_count_phandle_with_args(np, "clocks", "#clock-cells") == 3) {
+               clk2 = of_clk_get(np, 1);
+               if (IS_ERR(clk2)) {
+                       pr_err("sp804: %s clock not found: %d\n", np->name,
+                               (int)PTR_ERR(clk2));
+                       clk2 = NULL;
+               }
+       } else
+               clk2 = clk1;
+
+       irq = irq_of_parse_and_map(np, 0);
+       if (irq <= 0)
+               goto err;
+
+       of_property_read_u32(np, "arm,sp804-has-irq", &irq_num);
+       if (irq_num == 2) {
+               __sp804_clockevents_init(base + TIMER_2_BASE, irq, clk2, name);
+               __sp804_clocksource_and_sched_clock_init(base, name, clk1, 1);
+       } else {
+               __sp804_clockevents_init(base, irq, clk1 , name);
+               __sp804_clocksource_and_sched_clock_init(base + TIMER_2_BASE,
+                                                        name, clk2, 1);
+       }
+       initialized = true;
+
+       return;
+err:
+       iounmap(base);
+}
+CLOCKSOURCE_OF_DECLARE(sp804, "arm,sp804", sp804_of_init);
+
+static void __init integrator_cp_of_init(struct device_node *np)
+{
+       static int init_count = 0;
+       void __iomem *base;
+       int irq;
+       const char *name = of_get_property(np, "compatible", NULL);
+       struct clk *clk;
+
+       base = of_iomap(np, 0);
+       if (WARN_ON(!base))
+               return;
+       clk = of_clk_get(np, 0);
+       if (WARN_ON(IS_ERR(clk)))
+               return;
+
+       /* Ensure timer is disabled */
+       writel(0, base + TIMER_CTRL);
+
+       if (init_count == 2 || !of_device_is_available(np))
+               goto err;
+
+       if (!init_count)
+               __sp804_clocksource_and_sched_clock_init(base, name, clk, 0);
+       else {
+               irq = irq_of_parse_and_map(np, 0);
+               if (irq <= 0)
+                       goto err;
+
+               __sp804_clockevents_init(base, irq, clk, name);
+       }
+
+       init_count++;
+       return;
+err:
+       iounmap(base);
+}
+CLOCKSOURCE_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
diff --git a/include/clocksource/timer-sp804.h b/include/clocksource/timer-sp804.h
new file mode 100644 (file)
index 0000000..1f8a1ca
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __CLKSOURCE_TIMER_SP804_H
+#define __CLKSOURCE_TIMER_SP804_H
+
+struct clk;
+
+void __sp804_clocksource_and_sched_clock_init(void __iomem *,
+                                             const char *, struct clk *, int);
+void __sp804_clockevents_init(void __iomem *, unsigned int,
+                             struct clk *, const char *);
+void sp804_timer_disable(void __iomem *);
+
+static inline void sp804_clocksource_init(void __iomem *base, const char *name)
+{
+       __sp804_clocksource_and_sched_clock_init(base, name, NULL, 0);
+}
+
+static inline void sp804_clocksource_and_sched_clock_init(void __iomem *base,
+                                                         const char *name)
+{
+       __sp804_clocksource_and_sched_clock_init(base, name, NULL, 1);
+}
+
+static inline void sp804_clockevents_init(void __iomem *base, unsigned int irq, const char *name)
+{
+       __sp804_clockevents_init(base, irq, NULL, name);
+
+}
+#endif