/*
- * Copyright (C) 2004-2006 Atmel Corporation
+ * Copyright (C) 2004-2007 Atmel Corporation
  *
  * Based on MIPS implementation arch/mips/kernel/time.c
  *   Copyright 2001 MontaVista Software Inc.
 #include <linux/init.h>
 #include <linux/profile.h>
 #include <linux/sysdev.h>
+#include <linux/err.h>
 
 #include <asm/div64.h>
 #include <asm/sysreg.h>
 #include <asm/io.h>
 #include <asm/sections.h>
 
-static cycle_t read_cycle_count(void)
+/* how many counter cycles in a jiffy? */
+static u32 cycles_per_jiffy;
+
+/* the count value for the next timer interrupt */
+static u32 expirelo;
+
+cycle_t __weak read_cycle_count(void)
 {
        return (cycle_t)sysreg_read(COUNT);
 }
 
-static struct clocksource clocksource_avr32 = {
+struct clocksource __weak clocksource_avr32 = {
        .name           = "avr32",
        .rating         = 350,
        .read           = read_cycle_count,
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
+irqreturn_t __weak timer_interrupt(int irq, void *dev_id);
+
+struct irqaction timer_irqaction = {
+       .handler        = timer_interrupt,
+       .flags          = IRQF_DISABLED,
+       .name           = "timer",
+};
+
 /*
  * By default we provide the null RTC ops
  */
 static unsigned long null_rtc_get_time(void)
 {
-       return mktime(2004, 1, 1, 0, 0, 0);
+       return mktime(2007, 1, 1, 0, 0, 0);
 }
 
 static int null_rtc_set_time(unsigned long sec)
 static unsigned long (*rtc_get_time)(void) = null_rtc_get_time;
 static int (*rtc_set_time)(unsigned long) = null_rtc_set_time;
 
-/* how many counter cycles in a jiffy? */
-static unsigned long cycles_per_jiffy;
-
-/* cycle counter value at the previous timer interrupt */
-static unsigned int timerhi, timerlo;
-
-/* the count value for the next timer interrupt */
-static unsigned int expirelo;
-
 static void avr32_timer_ack(void)
 {
-       unsigned int count;
+       u32 count;
 
        /* Ack this timer interrupt and set the next one */
        expirelo += cycles_per_jiffy;
+       /* setting COMPARE to 0 stops the COUNT-COMPARE */
        if (expirelo == 0) {
-               printk(KERN_DEBUG "expirelo == 0\n");
                sysreg_write(COMPARE, expirelo + 1);
        } else {
                sysreg_write(COMPARE, expirelo);
        }
 }
 
-static unsigned int avr32_hpt_read(void)
+int __weak avr32_hpt_init(void)
 {
-       return sysreg_read(COUNT);
+       int ret;
+       unsigned long mult, shift, count_hz;
+
+       count_hz = clk_get_rate(boot_cpu_data.clk);
+       shift = clocksource_avr32.shift;
+       mult = clocksource_hz2mult(count_hz, shift);
+       clocksource_avr32.mult = mult;
+
+       {
+               u64 tmp;
+
+               tmp = TICK_NSEC;
+               tmp <<= shift;
+               tmp += mult / 2;
+               do_div(tmp, mult);
+
+               cycles_per_jiffy = tmp;
+       }
+
+       ret = setup_irq(0, &timer_irqaction);
+       if (ret) {
+               pr_debug("timer: could not request IRQ 0: %d\n", ret);
+               return -ENODEV;
+       }
+
+       printk(KERN_INFO "timer: AT32AP COUNT-COMPARE at irq 0, "
+                       "%lu.%03lu MHz\n",
+                       ((count_hz + 500) / 1000) / 1000,
+                       ((count_hz + 500) / 1000) % 1000);
+
+       return 0;
 }
 
 /*
  * Taken from MIPS c0_hpt_timer_init().
  *
- * Why is it so complicated, and what is "count"?  My assumption is
- * that `count' specifies the "reference cycle", i.e. the cycle since
- * reset that should mean "zero". The reason COUNT is written twice is
- * probably to make sure we don't get any timer interrupts while we
- * are messing with the counter.
+ * The reason COUNT is written twice is probably to make sure we don't get any
+ * timer interrupts while we are messing with the counter.
  */
-static void avr32_hpt_init(unsigned int count)
+int __weak avr32_hpt_start(void)
 {
-       count = sysreg_read(COUNT) - count;
+       u32 count = sysreg_read(COUNT);
        expirelo = (count / cycles_per_jiffy + 1) * cycles_per_jiffy;
        sysreg_write(COUNT, expirelo - cycles_per_jiffy);
        sysreg_write(COMPARE, expirelo);
        sysreg_write(COUNT, count);
+
+       return 0;
 }
 
 /*
  *
  * In UP mode, it is invoked from the (global) timer_interrupt.
  */
-static void local_timer_interrupt(int irq, void *dev_id)
+void local_timer_interrupt(int irq, void *dev_id)
 {
        if (current->pid)
                profile_tick(CPU_PROFILING);
        update_process_times(user_mode(get_irq_regs()));
 }
 
-static irqreturn_t
-timer_interrupt(int irq, void *dev_id)
+irqreturn_t __weak timer_interrupt(int irq, void *dev_id)
 {
-       unsigned int count;
-
        /* ack timer interrupt and try to set next interrupt */
-       count = avr32_hpt_read();
        avr32_timer_ack();
 
-       /* Update timerhi/timerlo for intra-jiffy calibration */
-       timerhi += count < timerlo;     /* Wrap around */
-       timerlo = count;
-
        /*
         * Call the generic timer interrupt handler
         */
        return IRQ_HANDLED;
 }
 
-static struct irqaction timer_irqaction = {
-       .handler        = timer_interrupt,
-       .flags          = IRQF_DISABLED,
-       .name           = "timer",
-};
-
 void __init time_init(void)
 {
-       unsigned long mult, shift, count_hz;
        int ret;
 
+       /*
+        * Make sure we don't get any COMPARE interrupts before we can
+        * handle them.
+        */
+       sysreg_write(COMPARE, 0);
+
        xtime.tv_sec = rtc_get_time();
        xtime.tv_nsec = 0;
 
        set_normalized_timespec(&wall_to_monotonic,
                                -xtime.tv_sec, -xtime.tv_nsec);
 
-       printk("Before time_init: count=%08lx, compare=%08lx\n",
-              (unsigned long)sysreg_read(COUNT),
-              (unsigned long)sysreg_read(COMPARE));
-
-       count_hz = clk_get_rate(boot_cpu_data.clk);
-       shift = clocksource_avr32.shift;
-       mult = clocksource_hz2mult(count_hz, shift);
-       clocksource_avr32.mult = mult;
-
-       printk("Cycle counter: mult=%lu, shift=%lu\n", mult, shift);
-
-       {
-               u64 tmp;
-
-               tmp = TICK_NSEC;
-               tmp <<= shift;
-               tmp += mult / 2;
-               do_div(tmp, mult);
-
-               cycles_per_jiffy = tmp;
+       ret = avr32_hpt_init();
+       if (ret) {
+               pr_debug("timer: failed setup: %d\n", ret);
+               return;
        }
 
-       /* This sets up the high precision timer for the first interrupt. */
-       avr32_hpt_init(avr32_hpt_read());
-
-       printk("After time_init: count=%08lx, compare=%08lx\n",
-              (unsigned long)sysreg_read(COUNT),
-              (unsigned long)sysreg_read(COMPARE));
-
        ret = clocksource_register(&clocksource_avr32);
        if (ret)
-               printk(KERN_ERR
-                      "timer: could not register clocksource: %d\n", ret);
+               pr_debug("timer: could not register clocksource: %d\n", ret);
 
-       ret = setup_irq(0, &timer_irqaction);
-       if (ret)
-               printk("timer: could not request IRQ 0: %d\n", ret);
+       ret = avr32_hpt_start();
+       if (ret) {
+               pr_debug("timer: failed starting: %d\n", ret);
+               return;
+       }
 }
 
 static struct sysdev_class timer_class = {
 
 obj-y                          += at32ap.o clock.o intc.o extint.o pio.o hsmc.o
 obj-$(CONFIG_CPU_AT32AP7000)   += at32ap7000.o
+obj-$(CONFIG_CPU_AT32AP7000)   += time-tc.o
 
        clk_disable(&hmatrix_clk);
 }
 
+/* --------------------------------------------------------------------
+ *  System Timer/Counter (TC)
+ * -------------------------------------------------------------------- */
+static struct resource at32_systc0_resource[] = {
+       PBMEM(0xfff00c00),
+       IRQ(22),
+};
+struct platform_device at32_systc0_device = {
+       .name           = "systc",
+       .id             = 0,
+       .resource       = at32_systc0_resource,
+       .num_resources  = ARRAY_SIZE(at32_systc0_resource),
+};
+DEV_CLK(pclk, at32_systc0, pbb, 3);
+
 /* --------------------------------------------------------------------
  *  PIO
  * -------------------------------------------------------------------- */
        platform_device_register(&smc0_device);
        platform_device_register(&pdc_device);
 
+       platform_device_register(&at32_systc0_device);
+
        platform_device_register(&pio0_device);
        platform_device_register(&pio1_device);
        platform_device_register(&pio2_device);
        &pio2_mck,
        &pio3_mck,
        &pio4_mck,
+       &at32_systc0_pclk,
        &atmel_usart0_usart,
        &atmel_usart1_usart,
        &atmel_usart2_usart,
 
--- /dev/null
+/*
+ * Copyright (C) 2004-2007 Atmel Corporation
+ *
+ * Based on MIPS implementation arch/mips/kernel/time.c
+ *   Copyright 2001 MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/profile.h>
+#include <linux/sysdev.h>
+#include <linux/err.h>
+
+#include <asm/div64.h>
+#include <asm/sysreg.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+
+#include <asm/arch/time.h>
+
+/* how many counter cycles in a jiffy? */
+static u32 cycles_per_jiffy;
+
+/* the count value for the next timer interrupt */
+static u32 expirelo;
+
+/* the I/O registers of the TC module */
+static void __iomem *ioregs;
+
+cycle_t read_cycle_count(void)
+{
+       return (cycle_t)timer_read(ioregs, 0, CV);
+}
+
+struct clocksource clocksource_avr32 = {
+       .name           = "avr32",
+       .rating         = 342,
+       .read           = read_cycle_count,
+       .mask           = CLOCKSOURCE_MASK(16),
+       .shift          = 16,
+       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void avr32_timer_ack(void)
+{
+       u16 count = expirelo;
+
+       /* Ack this timer interrupt and set the next one, use a u16
+        * variable so it will wrap around correctly */
+       count += cycles_per_jiffy;
+       expirelo = count;
+       timer_write(ioregs, 0, RC, expirelo);
+
+       /* Check to see if we have missed any timer interrupts */
+       count = timer_read(ioregs, 0, CV);
+       if ((count - expirelo) < 0x7fff) {
+               expirelo = count + cycles_per_jiffy;
+               timer_write(ioregs, 0, RC, expirelo);
+       }
+}
+
+u32 avr32_hpt_read(void)
+{
+       return timer_read(ioregs, 0, CV);
+}
+
+static int avr32_timer_calc_div_and_set_jiffies(struct clk *pclk)
+{
+       unsigned int cycles_max = (clocksource_avr32.mask + 1) / 2;
+       unsigned int divs[] = { 4, 8, 16, 32 };
+       int divs_size = sizeof(divs) / sizeof(*divs);
+       int i = 0;
+       unsigned long count_hz;
+       unsigned long shift;
+       unsigned long mult;
+       int clock_div = -1;
+       u64 tmp;
+
+       shift = clocksource_avr32.shift;
+
+       do {
+               count_hz = clk_get_rate(pclk) / divs[i];
+               mult = clocksource_hz2mult(count_hz, shift);
+               clocksource_avr32.mult = mult;
+
+               tmp = TICK_NSEC;
+               tmp <<= shift;
+               tmp += mult / 2;
+               do_div(tmp, mult);
+
+               cycles_per_jiffy = tmp;
+       } while (cycles_per_jiffy > cycles_max && ++i < divs_size);
+
+       clock_div = i + 1;
+
+       if (clock_div > divs_size) {
+               pr_debug("timer: could not calculate clock divider\n");
+               return -EFAULT;
+       }
+
+       /* Set the clock divider */
+       timer_write(ioregs, 0, CMR, TIMER_BF(CMR_TCCLKS, clock_div));
+
+       return 0;
+}
+
+int avr32_hpt_init(unsigned int count)
+{
+       struct resource *regs;
+       struct clk *pclk;
+       int irq = -1;
+       int ret = 0;
+
+       ret = -ENXIO;
+
+       irq = platform_get_irq(&at32_systc0_device, 0);
+       if (irq < 0) {
+               pr_debug("timer: could not get irq\n");
+               goto out_error;
+       }
+
+       pclk = clk_get(&at32_systc0_device.dev, "pclk");
+       if (IS_ERR(pclk)) {
+               pr_debug("timer: could not get clk: %ld\n", PTR_ERR(pclk));
+               goto out_error;
+       }
+
+       regs = platform_get_resource(&at32_systc0_device, IORESOURCE_MEM, 0);
+       if (!regs) {
+               pr_debug("timer: could not get resource\n");
+               goto out_error_clk;
+       }
+
+       ioregs = ioremap(regs->start, regs->end - regs->start + 1);
+       if (!ioregs) {
+               pr_debug("timer: could not get ioregs\n");
+               goto out_error_clk;
+       }
+
+       ret = avr32_timer_calc_div_and_set_jiffies(pclk);
+       if (ret)
+               goto out_error_io;
+
+       ret = setup_irq(irq, &timer_irqaction);
+       if (ret) {
+               pr_debug("timer: could not request irq %d: %d\n",
+                               irq, ret);
+               goto out_error_io;
+       }
+
+       expirelo = (timer_read(ioregs, 0, CV) / cycles_per_jiffy + 1)
+               * cycles_per_jiffy;
+
+       /* Enable clock and interrupts on RC compare */
+       timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_CLKEN));
+       timer_write(ioregs, 0, IER, TIMER_BIT(IER_CPCS));
+       /* Set cycles to first interrupt */
+       timer_write(ioregs, 0,  RC, expirelo);
+
+       printk(KERN_INFO "timer: AT32AP system timer/counter at 0x%p irq %d\n",
+                       ioregs, irq);
+
+       return 0;
+
+out_error_io:
+       iounmap(ioregs);
+out_error_clk:
+       clk_put(pclk);
+out_error:
+       return ret;
+}
+
+int avr32_hpt_start(void)
+{
+       timer_write(ioregs, 0, CCR, TIMER_BIT(CCR_SWTRG));
+       return 0;
+}
+
+irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+       unsigned int sr = timer_read(ioregs, 0, SR);
+
+       if (sr & TIMER_BIT(SR_CPCS)) {
+               /* ack timer interrupt and try to set next interrupt */
+               avr32_timer_ack();
+
+               /*
+                * Call the generic timer interrupt handler
+                */
+               write_seqlock(&xtime_lock);
+               do_timer(1);
+               write_sequnlock(&xtime_lock);
+
+               /*
+                * In UP mode, we call local_timer_interrupt() to do profiling
+                * and process accounting.
+                *
+                * SMP is not supported yet.
+                */
+               local_timer_interrupt(irq, dev_id);
+
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
 
--- /dev/null
+/*
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * 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 _ASM_AVR32_ARCH_AT32AP_TIME_H
+#define _ASM_AVR32_ARCH_AT32AP_TIME_H
+
+#include <linux/platform_device.h>
+
+extern struct irqaction timer_irqaction;
+extern struct platform_device at32_systc0_device;
+extern void local_timer_interrupt(int irq, void *dev_id);
+
+#define TIMER_BCR                                      0x000000c0
+#define TIMER_BCR_SYNC                                          0
+#define TIMER_BMR                                      0x000000c4
+#define TIMER_BMR_TC0XC0S                                       0
+#define TIMER_BMR_TC1XC1S                                       2
+#define TIMER_BMR_TC2XC2S                                       4
+#define TIMER_CCR                                      0x00000000
+#define TIMER_CCR_CLKDIS                                        1
+#define TIMER_CCR_CLKEN                                                 0
+#define TIMER_CCR_SWTRG                                                 2
+#define TIMER_CMR                                      0x00000004
+#define TIMER_CMR_ABETRG                                       10
+#define TIMER_CMR_ACPA                                         16
+#define TIMER_CMR_ACPC                                         18
+#define TIMER_CMR_AEEVT                                                20
+#define TIMER_CMR_ASWTRG                                       22
+#define TIMER_CMR_BCPB                                         24
+#define TIMER_CMR_BCPC                                         26
+#define TIMER_CMR_BEEVT                                                28
+#define TIMER_CMR_BSWTRG                                       30
+#define TIMER_CMR_BURST                                                 4
+#define TIMER_CMR_CLKI                                          3
+#define TIMER_CMR_CPCDIS                                        7
+#define TIMER_CMR_CPCSTOP                                       6
+#define TIMER_CMR_CPCTRG                                       14
+#define TIMER_CMR_EEVT                                         10
+#define TIMER_CMR_EEVTEDG                                       8
+#define TIMER_CMR_ENETRG                                       12
+#define TIMER_CMR_ETRGEDG                                       8
+#define TIMER_CMR_LDBDIS                                        7
+#define TIMER_CMR_LDBSTOP                                       6
+#define TIMER_CMR_LDRA                                         16
+#define TIMER_CMR_LDRB                                         18
+#define TIMER_CMR_TCCLKS                                        0
+#define TIMER_CMR_WAVE                                         15
+#define TIMER_CMR_WAVSEL                                       13
+#define TIMER_CV                                       0x00000010
+#define TIMER_CV_CV                                             0
+#define TIMER_IDR                                      0x00000028
+#define TIMER_IDR_COVFS                                                 0
+#define TIMER_IDR_CPAS                                          2
+#define TIMER_IDR_CPBS                                          3
+#define TIMER_IDR_CPCS                                          4
+#define TIMER_IDR_ETRGS                                                 7
+#define TIMER_IDR_LDRAS                                                 5
+#define TIMER_IDR_LDRBS                                                 6
+#define TIMER_IDR_LOVRS                                                 1
+#define TIMER_IER                                      0x00000024
+#define TIMER_IER_COVFS                                                 0
+#define TIMER_IER_CPAS                                          2
+#define TIMER_IER_CPBS                                          3
+#define TIMER_IER_CPCS                                          4
+#define TIMER_IER_ETRGS                                                 7
+#define TIMER_IER_LDRAS                                                 5
+#define TIMER_IER_LDRBS                                                 6
+#define TIMER_IER_LOVRS                                                 1
+#define TIMER_IMR                                      0x0000002c
+#define TIMER_IMR_COVFS                                                 0
+#define TIMER_IMR_CPAS                                          2
+#define TIMER_IMR_CPBS                                          3
+#define TIMER_IMR_CPCS                                          4
+#define TIMER_IMR_ETRGS                                                 7
+#define TIMER_IMR_LDRAS                                                 5
+#define TIMER_IMR_LDRBS                                                 6
+#define TIMER_IMR_LOVRS                                                 1
+#define TIMER_RA                                       0x00000014
+#define TIMER_RA_RA                                             0
+#define TIMER_RB                                       0x00000018
+#define TIMER_RB_RB                                             0
+#define TIMER_RC                                       0x0000001c
+#define TIMER_RC_RC                                             0
+#define TIMER_SR                                       0x00000020
+#define TIMER_SR_CLKSTA                                                16
+#define TIMER_SR_COVFS                                          0
+#define TIMER_SR_CPAS                                           2
+#define TIMER_SR_CPBS                                           3
+#define TIMER_SR_CPCS                                           4
+#define TIMER_SR_ETRGS                                          7
+#define TIMER_SR_LDRAS                                          5
+#define TIMER_SR_LDRBS                                          6
+#define TIMER_SR_LOVRS                                          1
+#define TIMER_SR_MTIOA                                         17
+#define TIMER_SR_MTIOB                                         18
+
+/* Bit manipulation macros */
+#define TIMER_BIT(name)                (1 << TIMER_##name)
+#define TIMER_BF(name,value)   ((value) << TIMER_##name)
+
+/* Register access macros */
+#define timer_read(port,instance,reg) \
+       __raw_readl(port + (0x40 * instance) + TIMER_##reg)
+#define timer_write(port,instance,reg,value) \
+       __raw_writel((value), port + (0x40 * instance) + TIMER_##reg)
+
+#endif /* _ASM_AVR32_ARCH_AT32AP_TIME_H */