This patch replace the lm32/milkymist custom timer code with a clocksource
and clockevents driver for the milkymist sysctl timers.

Timer 0 is used for the clockevents and can be either used in oneshot or
continious mode.
Timer 1 is used in free-running mode as a 32-bit clocksource.

Signed-off-by: Lars-Peter Clausen <[email protected]>
---
 arch/lm32/Kconfig                 |    6 +-
 arch/lm32/Kconfig.cpu             |    4 -
 arch/lm32/include/asm/hw/sysctl.h |    6 +
 arch/lm32/include/asm/timex.h     |    6 +-
 arch/lm32/kernel/time.c           |  219 ++++++++++++++++++++++---------------
 5 files changed, 145 insertions(+), 96 deletions(-)

diff --git a/arch/lm32/Kconfig b/arch/lm32/Kconfig
index 029d976..66c789b 100644
--- a/arch/lm32/Kconfig
+++ b/arch/lm32/Kconfig
@@ -49,7 +49,7 @@ config GENERIC_HARDIRQS
 
 config GENERIC_CLOCKEVENTS
        bool
-       default n
+       default y
 
 config SELECT_MEMORY_MODEL
        bool
@@ -79,6 +79,10 @@ source "init/Kconfig"
 
 source "arch/lm32/Kconfig.cpu"
 
+source "kernel/time/Kconfig"
+
+source "kernel/Kconfig.hz"
+
 source "mm/Kconfig"
 
 menu "Executable file formats"
diff --git a/arch/lm32/Kconfig.cpu b/arch/lm32/Kconfig.cpu
index 1dab1c6..368030b 100644
--- a/arch/lm32/Kconfig.cpu
+++ b/arch/lm32/Kconfig.cpu
@@ -54,10 +54,6 @@ config TEXT_OFFSET
        help
          Define where the kernel will be loaded to and executed from.
 
-config HZ
-       int
-       default 100
-
 config EARLY_PRINTK
        bool "Early printk"
        default y
diff --git a/arch/lm32/include/asm/hw/sysctl.h 
b/arch/lm32/include/asm/hw/sysctl.h
index 78d2492..ba6a1da 100644
--- a/arch/lm32/include/asm/hw/sysctl.h
+++ b/arch/lm32/include/asm/hw/sysctl.h
@@ -30,6 +30,12 @@
 #define CSR_TIMER1_COMPARE     (0xe0001024)
 #define CSR_TIMER1_COUNTER     (0xe0001028)
 
+#define CSR_TIMER(id, reg) (0xe0001010 + (0x10 * (id)) + (reg))
+
+#define CSR_TIMER_CONTROL(id) CSR_TIMER(id, 0x0)
+#define CSR_TIMER_COMPARE(id) CSR_TIMER(id, 0x4)
+#define CSR_TIMER_COUNTER(id) CSR_TIMER(id, 0x8)
+
 #define TIMER_ENABLE           (0x01)
 #define TIMER_AUTORESTART      (0x02)
 
diff --git a/arch/lm32/include/asm/timex.h b/arch/lm32/include/asm/timex.h
index bfe5b22d7..1d82420 100644
--- a/arch/lm32/include/asm/timex.h
+++ b/arch/lm32/include/asm/timex.h
@@ -32,11 +32,7 @@
 
 #endif /* __KERNEL__ */
 
-typedef unsigned long long cycles_t;
-
+typedef unsigned long cycles_t;
 cycles_t get_cycles(void);
 
-void lm32_systimer_ack(void);
-void lm32_systimer_program(int periodic, cycles_t cyc);
-
 #endif /*  _ASM_TIMEX_H */
diff --git a/arch/lm32/kernel/time.c b/arch/lm32/kernel/time.c
index 08d54db..3edcaa7 100644
--- a/arch/lm32/kernel/time.c
+++ b/arch/lm32/kernel/time.c
@@ -1,117 +1,164 @@
 /*
- * (C) Copyright 2007
- *     Theobroma Systems <www.theobroma-systems.com>
+ *  Copyright (C) 2011, Lars-Peter Clausen <[email protected]>
+ *  Clockevent and clocksource driver for the Milkymist sysctl timers
  *
- * See file CREDITS for list of people who contributed to this
- * project.
+ *  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 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.
+ *  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.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * 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
- */
-
-/*
- * Based on
- *
- * linux/arch/m68knommu/kernel/time.c
- *
- *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
  */
 
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/sched.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/profile.h>
-#include <linux/time.h>
+
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
 #include <linux/timex.h>
-#include <linux/interrupt.h>
 
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/irq_regs.h>
-#include <asm/setup.h>
-#include <asm/uaccess.h>
-#include <asm/hw/milkymist.h>
+#include <asm/hw/interrupts.h>
+#include <asm/hw/sysctl.h>
 
-#ifdef CONFIG_SMP
-extern void smp_local_timer_interrupt(void);
-#endif
+#define TIMER_CLOCKEVENT 0
+#define TIMER_CLOCKSOURCE 1
 
-cycles_t lm32_cycles = 0;
+static uint32_t milkymist_ticks_per_jiffy;
 
-static irqreturn_t timer_interrupt(int irq, void *timer_idx);
+static inline uint32_t milkymist_timer_get_counter(unsigned int timer)
+{
+       return ioread32be(CSR_TIMER_COUNTER(timer));
+}
 
-/* irq action description */
-static struct irqaction lm32_core_timer_irqaction = {
-       .name = "LM32 Timer Tick",
-       .flags = IRQF_DISABLED,
-       .handler = timer_interrupt,
-};
+static inline void milkymist_timer_set_counter(unsigned int timer, uint32_t 
value)
+{
+       iowrite32be(value, CSR_TIMER_COUNTER(timer));
+}
 
-/*
- * timer_interrupt() needs to call the "do_timer()"
- * routine every clocktick
- */
-static irqreturn_t timer_interrupt(int irq, void *arg)
+static inline uint32_t milkymist_timer_get_compare(unsigned int timer)
 {
-       lm32_cycles += in_be32((u32 *)CSR_TIMER0_COMPARE);
-       write_seqlock(&xtime_lock);
+       return ioread32be(CSR_TIMER_COMPARE(timer));
+}
 
-       do_timer(1);
-#ifndef CONFIG_SMP
-       update_process_times(user_mode(0));
-#endif
+static inline void milkymist_timer_set_compare(unsigned int timer, uint32_t 
value)
+{
+       iowrite32be(value, CSR_TIMER_COMPARE(timer));
+}
 
-#ifdef CONFIG_SMP
-       smp_local_timer_interrupt();
-       smp_send_timer();
-#endif
+static inline void milkymist_timer_disable(unsigned int timer)
+{
+       iowrite32be(0, CSR_TIMER_CONTROL(timer));
+}
 
-       if (current->pid)
-               profile_tick(CPU_PROFILING);
+static inline void milkymist_timer_enable(unsigned int timer, bool periodic)
+{
+       uint32_t val = TIMER_ENABLE;
+       if (periodic);
+               val |= TIMER_AUTORESTART;
+       iowrite32be(val, CSR_TIMER_CONTROL(timer));
+}
 
-       write_sequnlock(&xtime_lock);
-       return(IRQ_HANDLED);
+cycles_t get_cycles(void)
+{
+       return milkymist_timer_get_counter(TIMER_CLOCKSOURCE);
+}
+
+static cycle_t milkymist_clocksource_read(struct clocksource *cs)
+{
+       return get_cycles();
 }
 
-void time_init(void)
+static struct clocksource milkymist_clocksource = {
+       .name = "milkymist-timer",
+       .rating = 200,
+       .read = milkymist_clocksource_read,
+       .mask = CLOCKSOURCE_MASK(32),
+       .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static irqreturn_t milkymist_clockevent_irq(int irq, void *devid)
 {
-       lm32_systimer_program(1, cpu_frequency / HZ);
+       struct clock_event_device *cd = devid;
 
-       if( setup_irq(IRQ_SYSTMR, &lm32_core_timer_irqaction) )
-               panic("could not attach timer interrupt!");
+       if (cd->mode != CLOCK_EVT_MODE_PERIODIC)
+               milkymist_timer_disable(TIMER_CLOCKEVENT);
 
-       lm32_irq_unmask(IRQ_SYSTMR);
+       cd->event_handler(cd);
+
+       return IRQ_HANDLED;
 }
 
-cycles_t get_cycles(void)
+static void milkymist_clockevent_set_mode(enum clock_event_mode mode,
+       struct clock_event_device *cd)
 {
-       return lm32_cycles +
-               in_be32(CSR_TIMER0_COUNTER);
+       switch (mode) {
+       case CLOCK_EVT_MODE_PERIODIC:
+               milkymist_timer_disable(TIMER_CLOCKEVENT);
+               milkymist_timer_set_counter(TIMER_CLOCKEVENT, 0);
+               milkymist_timer_set_compare(TIMER_CLOCKEVENT, 
milkymist_ticks_per_jiffy);
+       case CLOCK_EVT_MODE_RESUME:
+               milkymist_timer_enable(TIMER_CLOCKEVENT, true);
+               break;
+       case CLOCK_EVT_MODE_ONESHOT:
+       case CLOCK_EVT_MODE_SHUTDOWN:
+               milkymist_timer_disable(TIMER_CLOCKEVENT);
+               break;
+       default:
+               break;
+       }
 }
 
-void lm32_systimer_program(int periodic, cycles_t cyc)
+static int milkymist_clockevent_set_next(unsigned long evt,
+       struct clock_event_device *cd)
 {
-       /* stop timer */
-       out_be32(CSR_TIMER0_CONTROL,0);
-       /* reset/configure timer */
-       out_be32(CSR_TIMER0_COUNTER,0);
-       out_be32(CSR_TIMER0_COMPARE,(int)cyc);
-       /* start timer */
-       out_be32(CSR_TIMER0_CONTROL,periodic ? TIMER_ENABLE|TIMER_AUTORESTART : 
TIMER_ENABLE);
+       milkymist_timer_set_counter(TIMER_CLOCKEVENT, 0);
+       milkymist_timer_set_compare(TIMER_CLOCKEVENT, evt);
+       milkymist_timer_enable(TIMER_CLOCKEVENT, false);
+
+       return 0;
+}
+
+static struct clock_event_device milkymist_clockevent = {
+       .name = "milkymist-timer",
+       .features = CLOCK_EVT_FEAT_PERIODIC,
+       .set_next_event = milkymist_clockevent_set_next,
+       .set_mode = milkymist_clockevent_set_mode,
+       .rating = 200,
+       .irq = IRQ_TIMER0,
+};
+
+static struct irqaction timer_irqaction = {
+       .handler        = milkymist_clockevent_irq,
+       .flags          = IRQF_TIMER,
+       .name           = "milkymist-timerirq",
+       .dev_id         = &milkymist_clockevent,
+};
+
+void __init time_init(void)
+{
+       int ret;
+
+       milkymist_ticks_per_jiffy = DIV_ROUND_CLOSEST(cpu_frequency, HZ);
+
+       clockevents_calc_mult_shift(&milkymist_clockevent, cpu_frequency, 5);
+       milkymist_clockevent.min_delta_ns = clockevent_delta2ns(100, 
&milkymist_clockevent);
+       milkymist_clockevent.max_delta_ns = clockevent_delta2ns(0xffff, 
&milkymist_clockevent);
+       milkymist_clockevent.cpumask = cpumask_of(0);
+
+       milkymist_timer_disable(TIMER_CLOCKSOURCE);
+       milkymist_timer_set_compare(TIMER_CLOCKSOURCE, 0xffffffff);
+       milkymist_timer_set_counter(TIMER_CLOCKSOURCE, 0);
+       milkymist_timer_enable(TIMER_CLOCKSOURCE, true);
+
+       clockevents_register_device(&milkymist_clockevent);
+
+       ret = clocksource_register_hz(&milkymist_clocksource, cpu_frequency);
+
+       if (ret)
+               printk(KERN_ERR "Failed to register clocksource: %d\n", ret);
+
+       setup_irq(IRQ_TIMER0, &timer_irqaction);
 }
-- 
1.7.2.3

_______________________________________________
http://lists.milkymist.org/listinfo.cgi/devel-milkymist.org
IRC: #milkymist@Freenode
Twitter: www.twitter.com/milkymistvj
Ideas? http://milkymist.uservoice.com

Reply via email to