Re: gtod/clocksource/clockevents documentation

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

 



Hello David,

Thanks for this patch you sent me. This patch will also increase the
clock resolution which is good in RT context.

But, unfortunately, I have some problems with compiling the kernel, as
this patch does not enable CONFIG_GENERIC_TIME, while the patch you
published earlier did.

Is there a special reason why you removed that?

I now get the following linker errors while using the RT-kernel. They
do not appear while using a non-RT kernel. (Those unresolved externals
rely on CONFIG_GENERIC_TIME)

====================================================================
kernel/built-in.o: In function
`do_sys_settimeofday':tsacct.c:(.text+0xad60): undefined reference to
`warp_check_clock_was_changed'
kernel/built-in.o: In function `do_sysinfo':tsacct.c:(.text+0x101d4):
undefined reference to `__get_nsec_offset'
kernel/built-in.o: In function
`timekeeping_resume':tsacct.c:(.text+0x10acc): undefined reference to
`warp_check_clock_was_changed'
kernel/built-in.o: In function
`ktime_get_ts':tsacct.c:(.text+0x1e498): undefined reference to
`__get_nsec_offset'
kernel/built-in.o: In function
`second_overflow':tsacct.c:(.text+0x1fd3c): undefined reference to
`warp_check_clock_was_changed'
====================================================================

Kind Regards,

Remy Böhmer


2007/4/22, David Brownell <[email protected]>:
On Saturday 21 April 2007, Remy Bohmer wrote:
> Hello All,
>
> I need to implement a gtod/clocksource/clockevents implementation for
> the Atmel ARM AT91SAM9261 CPU, and I am looking for some kernel
> (interface) documentation about these mechanisms.
>
> I already investigated the 'examples'/implementations of other
> architectures in the kernel, but that did not really help me... I also
> looked at the patch for the AT91RM9200 CPU posted by David Brownell...

You know, I started that partly because I couldn't find enough
info to satisfy my curiousity about how the "new dyntick" was
expected to work ... ;)


> I hacked something which now makes the RT kernel to boot, but the
> time-of-day warps several minutes per second, back and forward in
> time... ;-)

Maybe you've got the scaling wrong on the clocksources?  The
concerns with the shift/mult stuff were particularly opaque;
some sanity checks might be worth adding, it's easy enough
to provide wrong values with exactly those failures mode, and
it's not immediately obvious what "good" values may be.


> So, Can anybody please give me pointer to some useful documentation
> and/or examples?

The best I found was www.kernel.org, current GIT trees.  There
were some old OLS papers too.

One thing I found lacking was the simple statement up front that
clocksources are just fixed-rate free running counters; and that
clockevents are just dual-mode (PIT or oneshot) timers.


> The reason behind this initiative is to make the RT-preempt patch
> compile and work on this architecture. Currently the GENERIC_TIME is
> not set, which results in several unresolved externals at compile
> time, and I believe the best way to fix this is to implement proper
> gtod/clocksource/clockevents to allow the enabling of GENERIC_TIME

Try the appended patch ... the clocksource should work for you,
giving GENERIC_TIME with maybe a bit of tweaking.

- Dave

=====================   CUT HERE
This defines a simple library to allocate at91 timer/counter blocks,
insulating drivers from CPU differences.  Making that work also implies
defining TC clocks for each processor, and handing a table of TC block
descriptors to that library.

It also defines a simple TC based clocksource.  Any available TC block
can be used as a higher precision clocksource than one based on the 32KHz
slow clock.  Its timebase is MCLK/8, usually 5-10 MHz.  The way it works
is to combine two 16-bit counters to get a 32-bit clocksource.

INCOMPLETE:
    (a) The clocksource is currently not efficient for suspend/resume;
        it doesn't have suspend or resume methods to shut down the TC
        clocks it uses.

    (b) AVR32 uses this same timer/counter block, albeit with different
        input clocks and prescalers ... the library to should work for
        those platforms too, but <linux/atmel_tc.h> has AT91-isms.

    (c) Recent versions of AVR32 code stopped using the architectural
        counter for the clocksource, and switched over to the TC block.
        Similar code should become fully sharable with at least the
        AT91sam926 chips.

Note that AT91rm9200 chips don't really have a need for this other than
the additional precision; their "system timer" module is more powerful
than the corresponding AT91sam926 logic, and can fully support the new
style dynamic tick framework.  (Although the timer IRQ path becomes so
long that DBGU consistently drops multiple character for things like
uparrow keys at 38400 baud...)

Signed-off-by: David Brownell <[email protected]>
---
 arch/arm/mach-at91/Kconfig       |   16 ++
 arch/arm/mach-at91/Makefile      |    1
 arch/arm/mach-at91/at91rm9200.c  |   36 ++++++
 arch/arm/mach-at91/at91sam9260.c |   36 ++++++
 arch/arm/mach-at91/at91sam9261.c |   27 ++++
 arch/arm/mach-at91/at91sam9263.c |   27 ++++
 arch/arm/mach-at91/generic.h     |    4
 arch/arm/mach-at91/tclib.c       |  220 +++++++++++++++++++++++++++++++++++++++
 include/linux/atmel_tc.h         |  187 +++++++++++++++++++++++++++++++++
 9 files changed, 554 insertions(+)

--- at91.orig/arch/arm/mach-at91/Kconfig        2007-03-08 20:17:01.000000000 -0800
+++ at91/arch/arm/mach-at91/Kconfig     2007-03-11 21:58:10.000000000 -0700
@@ -171,6 +171,22 @@ config AT91_PROGRAMMABLE_CLOCKS
          Select this if you need to program one or more of the PCK0..PCK3
          programmable clock outputs.

+config ATMEL_TCLIB
+       bool "Timer/Counter Library"
+       help
+         Select this if you want a library to allocate the Timer/Counter
+         blocks found on many Atmel processors.  This facilitates using
+         these modules despite processor differences.
+
+config AT91_TC_CLOCKSOURCE
+       bool "Timer/Counter Clocksource"
+       depends on ATMEL_TCLIB && ARCH_AT91
+       help
+         Select this to get a higher precision clocksource, with a
+         multi-MHz PLL as the base rather than a 32 Khz oscillator.
+         This prevents other drivers from using that TC block (which
+         is currently rare), and draws power for two of its channels.
+
 endmenu

 endif
--- at91.orig/arch/arm/mach-at91/Makefile       2007-03-08 19:50:34.000000000 -0800
+++ at91/arch/arm/mach-at91/Makefile    2007-03-08 20:17:01.000000000 -0800
@@ -8,6 +8,7 @@ obj-n           :=
 obj-           :=

 obj-$(CONFIG_PM)               += pm.o
+obj-$(CONFIG_ATMEL_TCLIB)      += tclib.o

 # CPU-specific support
 obj-$(CONFIG_ARCH_AT91RM9200)  += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o
--- at91.orig/arch/arm/mach-at91/generic.h      2007-03-08 19:50:34.000000000 -0800
+++ at91/arch/arm/mach-at91/generic.h   2007-03-11 21:58:09.000000000 -0700
@@ -26,6 +26,10 @@ struct sys_timer;
 extern struct sys_timer at91rm9200_timer;
 extern struct sys_timer at91sam926x_timer;

+/* Timer/Counter library */
+struct tc_block;
+extern void __init atmel_tc_init(struct tc_block *, unsigned n);
+
  /* Clocks */
 extern int __init at91_clock_init(unsigned long main_clock);
 struct device;
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ at91/arch/arm/mach-at91/tclib.c     2007-03-10 09:26:11.000000000 -0800
@@ -0,0 +1,220 @@
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <linux/ioport.h>
+#include <linux/atmel_tc.h>
+
+#include <asm/errno.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+#include "generic.h"
+
+
+/*
+ * This is a thin library to solve the problem of how to portably allocate
+ * one of the TC modules.  For simplicitly, it doesn't currently expect to
+ * share individual timers between different drivers.
+ *
+ * And at the end of this file there's an optional clocksource.  That may be
+ * of interest in cases where more precision is needed, or with sam92xx chips
+ * where no better timer is available.
+ */
+
+static struct tc_block *blocks;
+static unsigned nblocks;
+
+static DEFINE_MUTEX(alloc_lock);
+
+struct tc_block *tc_allocate(int blk, const char *name)
+{
+       struct tc_block *tc;
+
+       mutex_lock(&alloc_lock);
+       if (blk >= 0) {
+               if (blocks[blk].ioaddr)
+                       goto full;
+       } else {
+               for (blk = 0; blk < nblocks; blk++) {
+                       if (blocks[blk].ioaddr)
+                               continue;
+                       break;
+               }
+               if (blk == nblocks)
+                       goto full;
+       }
+       tc = &blocks[blk];
+
+       if (!request_mem_region(tc->physaddr, 256, name))
+               goto full;
+
+       tc->ioaddr = ioremap(tc->physaddr, 256);
+       if (!tc->ioaddr)
+               goto requested;
+
+       pr_debug("%s: tc%d\n", __FUNCTION__, blk);
+       mutex_unlock(&alloc_lock);
+       return tc;
+
+requested:
+       release_mem_region(tc->physaddr, 256);
+full:
+       pr_debug("%s: alloc FAIL\n", __FUNCTION__);
+       mutex_unlock(&alloc_lock);
+       return NULL;
+}
+
+void tc_free(struct tc_block *tc)
+{
+       int blk;
+
+       for (blk = 0; blk < nblocks; blk++) {
+               if (tc->ioaddr == blocks[blk].ioaddr)
+                       break;
+       }
+       if (blk == nblocks)
+               return;
+
+       mutex_lock(&alloc_lock);
+       pr_debug("%s: tc%d\n", __FUNCTION__, blk);
+       iounmap(tc->ioaddr);
+       tc->ioaddr = NULL;
+       release_mem_region(tc->physaddr, 256);
+       mutex_unlock(&alloc_lock);
+}
+
+void __init atmel_tc_init(struct tc_block *tcblocks, unsigned n)
+{
+       blocks = tcblocks;
+       nblocks = n;
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+#ifdef CONFIG_AT91_TC_CLOCKSOURCE
+
+#include <linux/clocksource.h>
+
+/* Build a free-running 32 bit timer with a decent base rate of MCK/8
+ * (at around 5-10 MHz) by chaining two 16 bit TC timers.  This rolls
+ * over in around ten minutes.  (There seems to be little point in using
+ * CLOCK1, at MCK/2, given how long it takes to read the cycle count.
+ * This way, the reported precision reasonably matches its accuracy.)
+ *
+ * Example: rm9200 board MCK 46 Mhz, TC clk at 5.76 MHz or ~174 ns/tick.
+ * The first timer rolls over every 11.4 msec, incrementing the second
+ * counter at ~87 Hz.
+ *
+ * NOTE that the third timer does NOT make a very good clockevent source;
+ * sixteen bit counters are wasted silicon in this context.  In periodic
+ * mode, slightly higher MCK would prevent HZ=100 from working.  (The PIT
+ * timer in any AT91 chip is a better answer.)  For oneshot mode, it can't
+ * handle intervals long enough (up to a few seconds) for good "tickless"
+ * operation.  Using CLOCK4 or SLCK barely helps.
+ */
+
+static struct tc_block *tc;
+
+static cycle_t tc_get_cycles(void)
+{
+       union {
+               u32     word;
+               u16     half[2];
+       }               u;
+       unsigned long   flags;
+       void __iomem    *tcaddr = tc->ioaddr;
+
+       local_irq_save(flags);
+retry:
+       u.half[1] = __raw_readl(tcaddr + TC_CHAN(1, CV));
+       u.half[0] = __raw_readl(tcaddr + TC_CHAN(0, CV));
+       if (u.half[1] != __raw_readl(tcaddr + TC_CHAN(1, CV)))
+               goto retry;
+       local_irq_restore(flags);
+       return u.word;
+}
+
+static struct clocksource tc_clksrc = {
+       .name           = "tc_clksrc",
+       .rating         = 250,
+       .read           = tc_get_cycles,
+       .mask           = CLOCKSOURCE_MASK(32),
+       .shift          = 18,
+       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+
+/* REVISIT behavior during system suspend states... we should disable all
+ * clocks and save the power, especially when the main oscillator will be
+ * disabled!  This implies defining and using a driver model device node.
+ */
+
+
+/* Init this late, so board-specific code has a chance to grab hardware.
+ * All we really care about is having two timers to chain; but sometimes
+ * TC modules will be used by drivers hooked up to external hardware.
+ */
+static int __init tc_clksrc_init(void)
+{
+       static char bootinfo[] __initdata
+               = KERN_INFO "%s: tc%d at %d.%03d MHz\n";
+
+       int value;
+       void __iomem *addr;
+
+       tc = tc_allocate(-1, tc_clksrc.name);
+       if (tc == NULL) {
+               pr_debug("can't alloc TC for clocksource\n");
+               return -ENODEV;
+       }
+
+       /* how fast will we be counting? */
+       value = (int) clk_get_rate(tc->tc0_clk) / 8;
+       tc_clksrc.mult = clocksource_hz2mult(value, tc_clksrc.shift);
+
+       printk(bootinfo, tc_clksrc.name, tc - blocks,
+                       value/1000000, (value % 1000000) / 1000);
+
+       /* channel 0:  waveform mode, input mclk/8, clock TIOA0 on overflow */
+       addr = tc->ioaddr;
+       clk_enable(tc->tc0_clk);
+       __raw_writel(AT91_TC_TIMER_CLOCK2               /* input: mclk/8 */
+                       | AT91_TC_WAVE
+                       | AT91_TC_WAVESEL_UP            /* free-run */
+                       | AT91_TC_ACPA_SET              /* TIOA0 rises at 0 */
+                       | AT91_TC_ACPC_CLEAR,           /* (duty cycle 50%) */
+                       addr + TC_CHAN(0, CMR));
+       __raw_writel(0x0000, addr + TC_CHAN(0, RA));
+       __raw_writel(0x8000, addr + TC_CHAN(0, RC));
+       __raw_writel(0xff, addr + TC_CHAN(0, IDR));     /* no irqs */
+       __raw_writel(AT91_TC_CLKEN, addr + TC_CHAN(0, CCR));
+
+       /* channel 1:  waveform mode, input TIOA0 */
+       clk_enable(tc->tc1_clk);        /* REVISIT: is this clock needed?? */
+       __raw_writel(AT91_TC_XC1                        /* input: TIOA0 */
+                       | AT91_TC_WAVE
+                       | AT91_TC_WAVESEL_UP,           /* free-run */
+                       addr + TC_CHAN(1, CMR));
+       __raw_writel(0xff, addr + TC_CHAN(1, IDR));     /* no irqs */
+       __raw_writel(AT91_TC_CLKEN, addr + TC_CHAN(1, CCR));
+
+       /* channel 2: UNUSED */
+
+       /* chain channel 0 to channel 1, then reset all the timers */
+       __raw_writel(AT91_TC_TC0XC0S_NONE
+                       | AT91_TC_TC1XC1S_TIOA0
+                       | AT91_TC_TC2XC2S_NONE,
+                       tc->ioaddr + AT91_TC_BMR);
+       __raw_writel(AT91_TC_SYNC, tc->ioaddr + AT91_TC_BCR);
+
+       /* and away we go! */
+       clocksource_register(&tc_clksrc);
+
+       return 0;
+}
+late_initcall(tc_clksrc_init);
+
+#endif /* CONFIG_AT91_TC_CLOCKSOURCE */
--- at91.orig/arch/arm/mach-at91/at91rm9200.c   2007-03-08 19:50:34.000000000 -0800
+++ at91/arch/arm/mach-at91/at91rm9200.c        2007-03-10 09:16:49.000000000 -0800
@@ -248,6 +248,39 @@ static void at91rm9200_reset(void)
        at91_sys_write(AT91_ST_CR, AT91_ST_WDRST);
 }

+/* --------------------------------------------------------------------
+ *  Timer/Counter library initialization
+ * -------------------------------------------------------------------- */
+#ifdef CONFIG_ATMEL_TCLIB
+#include <linux/atmel_tc.h>
+
+static struct tc_block tcblocks[] = {
+       [0] = {
+               .physaddr = AT91RM9200_BASE_TCB0,
+               .tc0_clk = &tc0_clk,
+               .tc0_irq = AT91RM9200_ID_TC0,
+               .tc1_clk = &tc1_clk,
+               .tc1_irq = AT91RM9200_ID_TC1,
+               .tc2_clk = &tc2_clk,
+               .tc2_irq = AT91RM9200_ID_TC2,
+       },
+       [1] = {
+               .physaddr = AT91RM9200_BASE_TCB1,
+               .tc0_clk = &tc3_clk,
+               .tc0_irq = AT91RM9200_ID_TC3,
+               .tc1_clk = &tc4_clk,
+               .tc1_irq = AT91RM9200_ID_TC4,
+               .tc2_clk = &tc5_clk,
+               .tc2_irq = AT91RM9200_ID_TC5,
+       },
+};
+
+#define at91rm9200_tc_init()   atmel_tc_init(tcblocks, ARRAY_SIZE(tcblocks))
+
+#else
+#define at91rm9200_tc_init()   do{}while(0)
+#endif
+

 /* --------------------------------------------------------------------
  *  AT91RM9200 processor initialization
@@ -271,6 +304,9 @@ void __init at91rm9200_initialize(unsign

        /* Initialize GPIO subsystem */
        at91_gpio_init(at91rm9200_gpio, banks);
+
+       /* Initialize TC blocks */
+       at91rm9200_tc_init();
 }


--- at91.orig/arch/arm/mach-at91/at91sam9260.c  2007-03-08 19:50:34.000000000 -0800
+++ at91/arch/arm/mach-at91/at91sam9260.c       2007-03-10 09:16:56.000000000 -0800
@@ -262,6 +262,39 @@ static void at91sam9260_reset(void)
        at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST);
 }

+/* --------------------------------------------------------------------
+ *  Timer/Counter library initialization
+ * -------------------------------------------------------------------- */
+#ifdef CONFIG_ATMEL_TCLIB
+#include <linux/atmel_tc.h>
+
+static struct tc_block tcblocks[] = {
+       [0] = {
+               .physaddr = AT91SAM9260_BASE_TCB0,
+               .tc0_clk = &tc0_clk,
+               .tc0_irq = AT91SAM9260_ID_TC0,
+               .tc1_clk = &tc1_clk,
+               .tc1_irq = AT91SAM9260_ID_TC1,
+               .tc2_clk = &tc2_clk,
+               .tc2_irq = AT91SAM9260_ID_TC2,
+       },
+       [1] = {
+               .physaddr = AT91SAM9260_BASE_TCB1,
+               .tc0_clk = &tc3_clk,
+               .tc0_irq = AT91SAM9260_ID_TC3,
+               .tc1_clk = &tc4_clk,
+               .tc1_irq = AT91SAM9260_ID_TC4,
+               .tc2_clk = &tc5_clk,
+               .tc2_irq = AT91SAM9260_ID_TC5,
+       },
+};
+
+#define at91sam9260_tc_init()  atmel_tc_init(tcblocks, ARRAY_SIZE(tcblocks))
+
+#else
+#define at91sam9260_tc_init()  do{}while(0)
+#endif
+

 /* --------------------------------------------------------------------
  *  AT91SAM9260 processor initialization
@@ -310,6 +343,9 @@ void __init at91sam9260_initialize(unsig

        /* Register GPIO subsystem */
        at91_gpio_init(at91sam9260_gpio, 3);
+
+       /* Initialize TC blocks */
+       at91sam9260_tc_init();
 }

 /* --------------------------------------------------------------------
--- at91.orig/arch/arm/mach-at91/at91sam9261.c  2007-03-08 19:50:34.000000000 -0800
+++ at91/arch/arm/mach-at91/at91sam9261.c       2007-03-10 09:16:58.000000000 -0800
@@ -230,6 +230,30 @@ static void at91sam9261_reset(void)


 /* --------------------------------------------------------------------
+ *  Timer/Counter library initialization
+ * -------------------------------------------------------------------- */
+#ifdef CONFIG_ATMEL_TCLIB
+#include <linux/atmel_tc.h>
+
+static struct tc_block tcblocks[] = {
+       [0] = {
+               .physaddr = AT91SAM9261_BASE_TCB0,
+               .tc0_clk = &tc0_clk,
+               .tc0_irq = AT91SAM9261_ID_TC0,
+               .tc1_clk = &tc1_clk,
+               .tc1_irq = AT91SAM9261_ID_TC1,
+               .tc2_clk = &tc2_clk,
+               .tc2_irq = AT91SAM9261_ID_TC2,
+       },
+};
+
+#define at91sam9261_tc_init()  atmel_tc_init(tcblocks, ARRAY_SIZE(tcblocks))
+
+#else
+#define at91sam9261_tc_init()  do{}while(0)
+#endif
+
+/* --------------------------------------------------------------------
  *  AT91SAM9261 processor initialization
  * -------------------------------------------------------------------- */

@@ -250,6 +274,9 @@ void __init at91sam9261_initialize(unsig

        /* Register GPIO subsystem */
        at91_gpio_init(at91sam9261_gpio, 3);
+
+       /* Initialize TC blocks */
+       at91sam9261_tc_init();
 }

 /* --------------------------------------------------------------------
--- at91.orig/arch/arm/mach-at91/at91sam9263.c  2007-03-08 19:50:34.000000000 -0800
+++ at91/arch/arm/mach-at91/at91sam9263.c       2007-03-10 09:17:02.000000000 -0800
@@ -237,6 +237,30 @@ static void at91sam9263_reset(void)


 /* --------------------------------------------------------------------
+ *  Timer/Counter library initialization
+ * -------------------------------------------------------------------- */
+#ifdef CONFIG_ATMEL_TCLIB
+#include <linux/atmel_tc.h>
+
+static struct tc_block tcblocks[] = {
+       [0] = {
+               .physaddr = AT91SAM9263_BASE_TCB0,
+               .tc0_clk = &tcb_clk,
+               .tc0_irq = AT91SAM9261_ID_TCB,
+               .tc1_clk = &tcb_clk,
+               .tc1_irq = AT91SAM9261_ID_TCB,
+               .tc2_clk = &tcb_clk,
+               .tc2_irq = AT91SAM9261_ID_TCB,
+       },
+};
+
+#define at91sam9263_tc_init()  atmel_tc_init(tcblocks, ARRAY_SIZE(tcblocks))
+
+#else
+#define at91sam9263_tc_init()  do{}while(0)
+#endif
+
+/* --------------------------------------------------------------------
  *  AT91SAM9263 processor initialization
  * -------------------------------------------------------------------- */

@@ -256,6 +280,9 @@ void __init at91sam9263_initialize(unsig

        /* Register GPIO subsystem */
        at91_gpio_init(at91sam9263_gpio, 5);
+
+       /* Initialize TC blocks */
+       at91sam9263_tc_init();
 }

 /* --------------------------------------------------------------------
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ at91/include/linux/atmel_tc.h       2007-03-08 20:17:01.000000000 -0800
@@ -0,0 +1,187 @@
+/*
+ * <linux/atmel_tc.h>
+ *
+ * Copyright (C) SAN People
+ *
+ * Timer/Counter Unit (TC) registers.
+ * Based on AT91RM9200 datasheet revision E.
+ *
+ * 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.
+ */
+
+#ifndef ATMEL_TC_H
+#define ATMEL_TC_H
+
+/*
+ * This is a simple library to allocate the 16-bit Timer/Counter blocks..
+ * It allocates either specific blocks (interfacing to external hardware),
+ * or any unused block..  At least initially, this doesn't expect to share
+ * timers in one block between drivers.
+ *
+ * Note that some chips have multiple TC blocks, while others don't.
+ * Also, some chips have one clock (and irq) for each timer, but others
+ * share one for the whole block.
+ */
+
+/* REVISIT AVR32 uses interprets CLOCK1/CLOCK2/.../CLOCK5 differently
+ * than AT91.  Like the rm9200 and sam9260, the ap7000 has two TC modules.
+ *
+ * FIXME remove AT91 prefixes
+ */
+
+#define TC_CHAN(idx, reg)      ((idx)*0x40 + AT91_TC_ ## reg)
+
+struct tc_block {
+       void __iomem    *ioaddr;
+       u32             physaddr;
+
+       struct clk      *tc0_clk;
+       int             tc0_irq;
+
+       struct clk      *tc1_clk;
+       int             tc1_irq;
+
+       struct clk      *tc2_clk;
+       int             tc2_irq;
+};
+
+/*
+ * Returned TC block is "on loan" to caller.
+ * Caller enables/disables TC clocks as needed (e.g. during suspend).
+ * BLOCK = 0, 1, etc; or negative to mean "any block"
+ */
+extern struct tc_block *tc_allocate(int block, const char *name);
+extern void tc_free(struct tc_block *tc);
+
+#define AT91_TC_BCR            0xc0            /* TC Block Control Register */
+#define                AT91_TC_SYNC            (1 << 0)        /* Synchro Command */
+
+#define AT91_TC_BMR            0xc4            /* TC Block Mode Register */
+#define                AT91_TC_TC0XC0S         (3 << 0)        /* External Clock Signal 0 Selection */
+#define                        AT91_TC_TC0XC0S_TCLK0           (0 << 0)
+#define                        AT91_TC_TC0XC0S_NONE            (1 << 0)
+#define                        AT91_TC_TC0XC0S_TIOA1           (2 << 0)
+#define                        AT91_TC_TC0XC0S_TIOA2           (3 << 0)
+#define                AT91_TC_TC1XC1S         (3 << 2)        /* External Clock Signal 1 Selection */
+#define                        AT91_TC_TC1XC1S_TCLK1           (0 << 2)
+#define                        AT91_TC_TC1XC1S_NONE            (1 << 2)
+#define                        AT91_TC_TC1XC1S_TIOA0           (2 << 2)
+#define                        AT91_TC_TC1XC1S_TIOA2           (3 << 2)
+#define                AT91_TC_TC2XC2S         (3 << 4)        /* External Clock Signal 2 Selection */
+#define                        AT91_TC_TC2XC2S_TCLK2           (0 << 4)
+#define                        AT91_TC_TC2XC2S_NONE            (1 << 4)
+#define                        AT91_TC_TC2XC2S_TIOA0           (2 << 4)
+#define                        AT91_TC_TC2XC2S_TIOA1           (3 << 4)
+
+
+#define AT91_TC_CCR            0x00            /* Channel Control Register */
+#define                AT91_TC_CLKEN           (1 << 0)        /* Counter Clock Enable Command */
+#define                AT91_TC_CLKDIS          (1 << 1)        /* Counter CLock Disable Command */
+#define                AT91_TC_SWTRG           (1 << 2)        /* Software Trigger Command */
+
+#define AT91_TC_CMR            0x04            /* Channel Mode Register */
+#define                AT91_TC_TCCLKS          (7 << 0)        /* Capture/Waveform Mode: Clock Selection */
+#define                        AT91_TC_TIMER_CLOCK1            (0 << 0)
+#define                        AT91_TC_TIMER_CLOCK2            (1 << 0)
+#define                        AT91_TC_TIMER_CLOCK3            (2 << 0)
+#define                        AT91_TC_TIMER_CLOCK4            (3 << 0)
+#define                        AT91_TC_TIMER_CLOCK5            (4 << 0)
+#define                        AT91_TC_XC0                     (5 << 0)
+#define                        AT91_TC_XC1                     (6 << 0)
+#define                        AT91_TC_XC2                     (7 << 0)
+#define                AT91_TC_CLKI            (1 << 3)        /* Capture/Waveform Mode: Clock Invert */
+#define                AT91_TC_BURST           (3 << 4)        /* Capture/Waveform Mode: Burst Signal Selection */
+#define                AT91_TC_LDBSTOP         (1 << 6)        /* Capture Mode: Counter Clock Stopped with TB Loading */
+#define                AT91_TC_LDBDIS          (1 << 7)        /* Capture Mode: Counter Clock Disable with RB Loading */
+#define                AT91_TC_ETRGEDG         (3 << 8)        /* Capture Mode: External Trigger Edge Selection */
+#define                AT91_TC_ABETRG          (1 << 10)       /* Capture Mode: TIOA or TIOB External Trigger Selection */
+#define                AT91_TC_CPCTRG          (1 << 14)       /* Capture Mode: RC Compare Trigger Enable */
+#define                AT91_TC_WAVE            (1 << 15)       /* Capture/Waveform mode */
+#define                AT91_TC_LDRA            (3 << 16)       /* Capture Mode: RA Loading Selection */
+#define                AT91_TC_LDRB            (3 << 18)       /* Capture Mode: RB Loading Selection */
+
+#define                AT91_TC_CPCSTOP         (1 <<  6)       /* Waveform Mode: Counter Clock Stopped with RC Compare */
+#define                AT91_TC_CPCDIS          (1 <<  7)       /* Waveform Mode: Counter Clock Disable with RC Compare */
+#define                AT91_TC_EEVTEDG         (3 <<  8)       /* Waveform Mode: External Event Edge Selection */
+#define                        AT91_TC_EEVTEDG_NONE            (0 << 8)
+#define                        AT91_TC_EEVTEDG_RISING          (1 << 8)
+#define                        AT91_TC_EEVTEDG_FALLING         (2 << 8)
+#define                        AT91_TC_EEVTEDG_BOTH            (3 << 8)
+#define                AT91_TC_EEVT            (3 << 10)       /* Waveform Mode: External Event Selection */
+#define                        AT91_TC_EEVT_TIOB               (0 << 10)
+#define                        AT91_TC_EEVT_XC0                (1 << 10)
+#define                        AT91_TC_EEVT_XC1                (2 << 10)
+#define                        AT91_TC_EEVT_XC2                (3 << 10)
+#define                AT91_TC_ENETRG          (1 << 12)       /* Waveform Mode: External Event Trigger Enable */
+#define                AT91_TC_WAVESEL         (3 << 13)       /* Waveform Mode: Waveform Selection */
+#define                        AT91_TC_WAVESEL_UP              (0 << 13)
+#define                        AT91_TC_WAVESEL_UP_AUTO         (2 << 13)
+#define                        AT91_TC_WAVESEL_UPDOWN          (1 << 13)
+#define                        AT91_TC_WAVESEL_UPDOWN_AUTO     (3 << 13)
+#define                AT91_TC_ACPA            (3 << 16)       /* Waveform Mode: RA Compare Effect on TIOA */
+#define                        AT91_TC_ACPA_NONE               (0 << 16)
+#define                        AT91_TC_ACPA_SET                (1 << 16)
+#define                        AT91_TC_ACPA_CLEAR              (2 << 16)
+#define                        AT91_TC_ACPA_TOGGLE             (3 << 16)
+#define                AT91_TC_ACPC            (3 << 18)       /* Waveform Mode: RC Compre Effect on TIOA */
+#define                        AT91_TC_ACPC_NONE               (0 << 18)
+#define                        AT91_TC_ACPC_SET                (1 << 18)
+#define                        AT91_TC_ACPC_CLEAR              (2 << 18)
+#define                        AT91_TC_ACPC_TOGGLE             (3 << 18)
+#define                AT91_TC_AEEVT           (3 << 20)       /* Waveform Mode: External Event Effect on TIOA */
+#define                        AT91_TC_AEEVT_NONE              (0 << 20)
+#define                        AT91_TC_AEEVT_SET               (1 << 20)
+#define                        AT91_TC_AEEVT_CLEAR             (2 << 20)
+#define                        AT91_TC_AEEVT_TOGGLE            (3 << 20)
+#define                AT91_TC_ASWTRG          (3 << 22)       /* Waveform Mode: Software Trigger Effect on TIOA */
+#define                        AT91_TC_ASWTRG_NONE             (0 << 22)
+#define                        AT91_TC_ASWTRG_SET              (1 << 22)
+#define                        AT91_TC_ASWTRG_CLEAR            (2 << 22)
+#define                        AT91_TC_ASWTRG_TOGGLE           (3 << 22)
+#define                AT91_TC_BCPB            (3 << 24)       /* Waveform Mode: RB Compare Effect on TIOB */
+#define                        AT91_TC_BCPB_NONE               (0 << 24)
+#define                        AT91_TC_BCPB_SET                (1 << 24)
+#define                        AT91_TC_BCPB_CLEAR              (2 << 24)
+#define                        AT91_TC_BCPB_TOGGLE             (3 << 24)
+#define                AT91_TC_BCPC            (3 << 26)       /* Waveform Mode: RC Compare Effect on TIOB */
+#define                        AT91_TC_BCPC_NONE               (0 << 26)
+#define                        AT91_TC_BCPC_SET                (1 << 26)
+#define                        AT91_TC_BCPC_CLEAR              (2 << 26)
+#define                        AT91_TC_BCPC_TOGGLE             (3 << 26)
+#define                AT91_TC_BEEVT           (3 << 28)       /* Waveform Mode: External Event Effect on TIOB */
+#define                        AT91_TC_BEEVT_NONE              (0 << 28)
+#define                        AT91_TC_BEEVT_SET               (1 << 28)
+#define                        AT91_TC_BEEVT_CLEAR             (2 << 28)
+#define                        AT91_TC_BEEVT_TOGGLE            (3 << 28)
+#define                AT91_TC_BSWTRG          (3 << 30)       /* Waveform Mode: Software Trigger Effect on TIOB */
+#define                        AT91_TC_BSWTRG_NONE             (0 << 30)
+#define                        AT91_TC_BSWTRG_SET              (1 << 30)
+#define                        AT91_TC_BSWTRG_CLEAR            (2 << 30)
+#define                        AT91_TC_BSWTRG_TOGGLE           (3 << 30)
+
+#define AT91_TC_CV             0x10            /* Counter Value */
+#define AT91_TC_RA             0x14            /* Register A */
+#define AT91_TC_RB             0x18            /* Register B */
+#define AT91_TC_RC             0x1c            /* Register C */
+
+#define AT91_TC_SR             0x20            /* Status Register */
+#define                AT91_TC_COVFS           (1 <<  0)       /* Counter Overflow Status */
+#define                AT91_TC_LOVRS           (1 <<  1)       /* Load Overrun Status */
+#define                AT91_TC_CPAS            (1 <<  2)       /* RA Compare Status */
+#define                AT91_TC_CPBS            (1 <<  3)       /* RB Compare Status */
+#define                AT91_TC_CPCS            (1 <<  4)       /* RC Compare Status */
+#define                AT91_TC_LDRAS           (1 <<  5)       /* RA Loading Status */
+#define                AT91_TC_LDRBS           (1 <<  6)       /* RB Loading Status */
+#define                AT91_TC_ETRGS           (1 <<  7)       /* External Trigger Status */
+#define                AT91_TC_CLKSTA          (1 << 16)       /* Clock Enabling Status */
+#define                AT91_TC_MTIOA           (1 << 17)       /* TIOA Mirror */
+#define                AT91_TC_MTIOB           (1 << 18)       /* TIOB Mirror */
+
+#define AT91_TC_IER            0x24            /* Interrupt Enable Register */
+#define AT91_TC_IDR            0x28            /* Interrupt Disable Register */
+#define AT91_TC_IMR            0x2c            /* Interrupt Mask Register */
+
+#endif /* ATMEL_TC_H */



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux