Merge tag 'at91-drivers2' of git://github.com/at91linux/linux-at91 into next/drivers
authorOlof Johansson <olof@lixom.net>
Wed, 24 Sep 2014 04:58:35 +0000 (21:58 -0700)
committerOlof Johansson <olof@lixom.net>
Wed, 24 Sep 2014 04:58:50 +0000 (21:58 -0700)
Merge " Second drivers series for AT91/3.18" from Nicolas Ferre:

- move of the PIT (basic timer) from mach-at91 to its proper location:
  drivers/clocksource
- big cleanup of this driver along the way

* tag 'at91-drivers2' of git://github.com/at91linux/linux-at91:
  ARM: at91: PIT: Move the driver to drivers/clocksource
  ARM: at91: Give the PIT irq as an argument of at91sam926x_pit_init
  ARM: at91: Convert the boards to the init_time callback
  ARM: at91: soc: Add init_time callback
  ARM: at91: PIT: (Almost) remove the global variables
  ARM: at91: PIT: use request_irq instead of setup_irq
  ARM: at91: PIT: Use pr_fmt
  ARM: at91: PIT: Use consistent exit path in probe
  ARM: at91: dt: Remove init_time definitions
  ARM: at91: PIT: Rework probe functions
  ARM: at91: PIT: Use of_have_populated_dt instead of CONFIG_OF
  ARM: at91: PIT: Use DIV_ROUND_CLOSEST to compute the cycles
  ARM: at91: generic.h: Add include safe guards
  ARM: at91: PIT: Follow the general coding rules

Signed-off-by: Olof Johansson <olof@lixom.net>
32 files changed:
arch/arm/mach-at91/Kconfig
arch/arm/mach-at91/Makefile
arch/arm/mach-at91/at91sam9260.c
arch/arm/mach-at91/at91sam9261.c
arch/arm/mach-at91/at91sam9263.c
arch/arm/mach-at91/at91sam926x_time.c [deleted file]
arch/arm/mach-at91/at91sam9g45.c
arch/arm/mach-at91/at91sam9rl.c
arch/arm/mach-at91/board-afeb-9260v1.c
arch/arm/mach-at91/board-cam60.c
arch/arm/mach-at91/board-cpu9krea.c
arch/arm/mach-at91/board-dt-sam9.c
arch/arm/mach-at91/board-dt-sama5.c
arch/arm/mach-at91/board-flexibity.c
arch/arm/mach-at91/board-foxg20.c
arch/arm/mach-at91/board-gsia18s.c
arch/arm/mach-at91/board-pcontrol-g20.c
arch/arm/mach-at91/board-sam9-l9260.c
arch/arm/mach-at91/board-sam9260ek.c
arch/arm/mach-at91/board-sam9261ek.c
arch/arm/mach-at91/board-sam9263ek.c
arch/arm/mach-at91/board-sam9g20ek.c
arch/arm/mach-at91/board-sam9m10g45ek.c
arch/arm/mach-at91/board-sam9rlek.c
arch/arm/mach-at91/board-snapper9260.c
arch/arm/mach-at91/board-stamp9g20.c
arch/arm/mach-at91/generic.h
arch/arm/mach-at91/setup.c
arch/arm/mach-at91/soc.h
drivers/clocksource/Kconfig
drivers/clocksource/Makefile
drivers/clocksource/timer-atmel-pit.c [new file with mode: 0644]

index 6eb3c65..474c855 100644 (file)
@@ -33,15 +33,11 @@ config OLD_IRQ_AT91
        select MULTI_IRQ_HANDLER
        select SPARSE_IRQ
 
-config AT91_SAM9_TIME
-       bool
-
 config HAVE_AT91_SMD
        bool
 
 config SOC_AT91SAM9
        bool
-       select AT91_SAM9_TIME
        select ATMEL_AIC_IRQ if !OLD_IRQ_AT91
        select CPU_ARM926T
        select GENERIC_CLOCKEVENTS
@@ -50,7 +46,6 @@ config SOC_AT91SAM9
 
 config SOC_SAMA5
        bool
-       select AT91_SAM9_TIME
        select ATMEL_AIC5_IRQ
        select CPU_V7
        select GENERIC_CLOCKEVENTS
index 306c82b..61d04f9 100644 (file)
@@ -9,7 +9,6 @@ obj-            :=
 
 obj-$(CONFIG_OLD_IRQ_AT91)     += irq.o
 obj-$(CONFIG_OLD_CLK_AT91)     += clock.o
-obj-$(CONFIG_AT91_SAM9_TIME)   += at91sam926x_time.o
 obj-$(CONFIG_SOC_AT91SAM9)     += sam9_smc.o
 
 # CPU-specific support
index b6948d6..aab1f96 100644 (file)
@@ -440,6 +440,11 @@ static unsigned int at91sam9260_default_irq_priority[NR_AIC_IRQS] __initdata = {
        0,      /* Advanced Interrupt Controller */
 };
 
+static void __init at91sam9260_init_time(void)
+{
+       at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
+}
+
 AT91_SOC_START(at91sam9260)
        .map_io = at91sam9260_map_io,
        .default_irq_priority = at91sam9260_default_irq_priority,
@@ -449,4 +454,5 @@ AT91_SOC_START(at91sam9260)
        .register_clocks = at91sam9260_register_clocks,
        .register_devices = at91sam9260_register_devices,
        .init = at91sam9260_initialize,
+       .init_time = at91sam9260_init_time,
 AT91_SOC_END
index c07339e..a8bd359 100644 (file)
@@ -399,6 +399,11 @@ static unsigned int at91sam9261_default_irq_priority[NR_AIC_IRQS] __initdata = {
        0,      /* Advanced Interrupt Controller */
 };
 
+static void __init at91sam9261_init_time(void)
+{
+       at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
+}
+
 AT91_SOC_START(at91sam9261)
        .map_io = at91sam9261_map_io,
        .default_irq_priority = at91sam9261_default_irq_priority,
@@ -408,4 +413,5 @@ AT91_SOC_START(at91sam9261)
        .register_clocks = at91sam9261_register_clocks,
        .register_devices = at91sam9261_register_devices,
        .init = at91sam9261_initialize,
+       .init_time = at91sam9261_init_time,
 AT91_SOC_END
index 33ab06e..fbff228 100644 (file)
@@ -422,6 +422,11 @@ static unsigned int at91sam9263_default_irq_priority[NR_AIC_IRQS] __initdata = {
        0,      /* Advanced Interrupt Controller (IRQ1) */
 };
 
+static void __init at91sam9263_init_time(void)
+{
+       at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
+}
+
 AT91_SOC_START(at91sam9263)
        .map_io = at91sam9263_map_io,
        .default_irq_priority = at91sam9263_default_irq_priority,
@@ -430,4 +435,5 @@ AT91_SOC_START(at91sam9263)
        .register_clocks = at91sam9263_register_clocks,
        .register_devices = at91sam9263_register_devices,
        .init = at91sam9263_initialize,
+       .init_time = at91sam9263_init_time,
 AT91_SOC_END
diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c
deleted file mode 100644 (file)
index 0a9e2fc..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x
- *
- * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France
- * Revision     2005 M. Nicolas Diremdjian, ATMEL Rousset, France
- * Converted to ClockSource/ClockEvents by David Brownell.
- *
- * 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/interrupt.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/clockchips.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-
-#include <asm/mach/time.h>
-#include <mach/hardware.h>
-
-#define AT91_PIT_MR            0x00                    /* Mode Register */
-#define                AT91_PIT_PITIEN         (1 << 25)               /* Timer Interrupt Enable */
-#define                AT91_PIT_PITEN          (1 << 24)               /* Timer Enabled */
-#define                AT91_PIT_PIV            (0xfffff)               /* Periodic Interval Value */
-
-#define AT91_PIT_SR            0x04                    /* Status Register */
-#define                AT91_PIT_PITS           (1 << 0)                /* Timer Status */
-
-#define AT91_PIT_PIVR          0x08                    /* Periodic Interval Value Register */
-#define AT91_PIT_PIIR          0x0c                    /* Periodic Interval Image Register */
-#define                AT91_PIT_PICNT          (0xfff << 20)           /* Interval Counter */
-#define                AT91_PIT_CPIV           (0xfffff)               /* Inverval Value */
-
-#define PIT_CPIV(x)    ((x) & AT91_PIT_CPIV)
-#define PIT_PICNT(x)   (((x) & AT91_PIT_PICNT) >> 20)
-
-static u32 pit_cycle;          /* write-once */
-static u32 pit_cnt;            /* access only w/system irq blocked */
-static void __iomem *pit_base_addr __read_mostly;
-static struct clk *mck;
-
-static inline unsigned int pit_read(unsigned int reg_offset)
-{
-       return __raw_readl(pit_base_addr + reg_offset);
-}
-
-static inline void pit_write(unsigned int reg_offset, unsigned long value)
-{
-       __raw_writel(value, pit_base_addr + reg_offset);
-}
-
-/*
- * Clocksource:  just a monotonic counter of MCK/16 cycles.
- * We don't care whether or not PIT irqs are enabled.
- */
-static cycle_t read_pit_clk(struct clocksource *cs)
-{
-       unsigned long flags;
-       u32 elapsed;
-       u32 t;
-
-       raw_local_irq_save(flags);
-       elapsed = pit_cnt;
-       t = pit_read(AT91_PIT_PIIR);
-       raw_local_irq_restore(flags);
-
-       elapsed += PIT_PICNT(t) * pit_cycle;
-       elapsed += PIT_CPIV(t);
-       return elapsed;
-}
-
-static struct clocksource pit_clk = {
-       .name           = "pit",
-       .rating         = 175,
-       .read           = read_pit_clk,
-       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
-};
-
-
-/*
- * Clockevent device:  interrupts every 1/HZ (== pit_cycles * MCK/16)
- */
-static void
-pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
-{
-       switch (mode) {
-       case CLOCK_EVT_MODE_PERIODIC:
-               /* update clocksource counter */
-               pit_cnt += pit_cycle * PIT_PICNT(pit_read(AT91_PIT_PIVR));
-               pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN
-                               | AT91_PIT_PITIEN);
-               break;
-       case CLOCK_EVT_MODE_ONESHOT:
-               BUG();
-               /* FALLTHROUGH */
-       case CLOCK_EVT_MODE_SHUTDOWN:
-       case CLOCK_EVT_MODE_UNUSED:
-               /* disable irq, leaving the clocksource active */
-               pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN);
-               break;
-       case CLOCK_EVT_MODE_RESUME:
-               break;
-       }
-}
-
-static void at91sam926x_pit_suspend(struct clock_event_device *cedev)
-{
-       /* Disable timer */
-       pit_write(AT91_PIT_MR, 0);
-}
-
-static void at91sam926x_pit_reset(void)
-{
-       /* Disable timer and irqs */
-       pit_write(AT91_PIT_MR, 0);
-
-       /* Clear any pending interrupts, wait for PIT to stop counting */
-       while (PIT_CPIV(pit_read(AT91_PIT_PIVR)) != 0)
-               cpu_relax();
-
-       /* Start PIT but don't enable IRQ */
-       pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN);
-}
-
-static void at91sam926x_pit_resume(struct clock_event_device *cedev)
-{
-       at91sam926x_pit_reset();
-}
-
-static struct clock_event_device pit_clkevt = {
-       .name           = "pit",
-       .features       = CLOCK_EVT_FEAT_PERIODIC,
-       .shift          = 32,
-       .rating         = 100,
-       .set_mode       = pit_clkevt_mode,
-       .suspend        = at91sam926x_pit_suspend,
-       .resume         = at91sam926x_pit_resume,
-};
-
-
-/*
- * IRQ handler for the timer.
- */
-static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
-{
-       /*
-        * irqs should be disabled here, but as the irq is shared they are only
-        * guaranteed to be off if the timer irq is registered first.
-        */
-       WARN_ON_ONCE(!irqs_disabled());
-
-       /* The PIT interrupt may be disabled, and is shared */
-       if ((pit_clkevt.mode == CLOCK_EVT_MODE_PERIODIC)
-                       && (pit_read(AT91_PIT_SR) & AT91_PIT_PITS)) {
-               unsigned nr_ticks;
-
-               /* Get number of ticks performed before irq, and ack it */
-               nr_ticks = PIT_PICNT(pit_read(AT91_PIT_PIVR));
-               do {
-                       pit_cnt += pit_cycle;
-                       pit_clkevt.event_handler(&pit_clkevt);
-                       nr_ticks--;
-               } while (nr_ticks);
-
-               return IRQ_HANDLED;
-       }
-
-       return IRQ_NONE;
-}
-
-static struct irqaction at91sam926x_pit_irq = {
-       .name           = "at91_tick",
-       .flags          = IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
-       .handler        = at91sam926x_pit_interrupt,
-       .irq            = NR_IRQS_LEGACY + AT91_ID_SYS,
-};
-
-#ifdef CONFIG_OF
-static struct of_device_id pit_timer_ids[] = {
-       { .compatible = "atmel,at91sam9260-pit" },
-       { /* sentinel */ }
-};
-
-static int __init of_at91sam926x_pit_init(void)
-{
-       struct device_node      *np;
-       int                     ret;
-
-       np = of_find_matching_node(NULL, pit_timer_ids);
-       if (!np)
-               goto err;
-
-       pit_base_addr = of_iomap(np, 0);
-       if (!pit_base_addr)
-               goto node_err;
-
-       mck = of_clk_get(np, 0);
-
-       /* Get the interrupts property */
-       ret = irq_of_parse_and_map(np, 0);
-       if (!ret) {
-               pr_crit("AT91: PIT: Unable to get IRQ from DT\n");
-               if (!IS_ERR(mck))
-                       clk_put(mck);
-               goto ioremap_err;
-       }
-       at91sam926x_pit_irq.irq = ret;
-
-       of_node_put(np);
-
-       return 0;
-
-ioremap_err:
-       iounmap(pit_base_addr);
-node_err:
-       of_node_put(np);
-err:
-       return -EINVAL;
-}
-#else
-static int __init of_at91sam926x_pit_init(void)
-{
-       return -EINVAL;
-}
-#endif
-
-/*
- * Set up both clocksource and clockevent support.
- */
-void __init at91sam926x_pit_init(void)
-{
-       unsigned long   pit_rate;
-       unsigned        bits;
-       int             ret;
-
-       mck = ERR_PTR(-ENOENT);
-
-       /* For device tree enabled device: initialize here */
-       of_at91sam926x_pit_init();
-
-       /*
-        * Use our actual MCK to figure out how many MCK/16 ticks per
-        * 1/HZ period (instead of a compile-time constant LATCH).
-        */
-       if (IS_ERR(mck))
-               mck = clk_get(NULL, "mck");
-
-       if (IS_ERR(mck))
-               panic("AT91: PIT: Unable to get mck clk\n");
-       pit_rate = clk_get_rate(mck) / 16;
-       pit_cycle = (pit_rate + HZ/2) / HZ;
-       WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0);
-
-       /* Initialize and enable the timer */
-       at91sam926x_pit_reset();
-
-       /*
-        * Register clocksource.  The high order bits of PIV are unused,
-        * so this isn't a 32-bit counter unless we get clockevent irqs.
-        */
-       bits = 12 /* PICNT */ + ilog2(pit_cycle) /* PIV */;
-       pit_clk.mask = CLOCKSOURCE_MASK(bits);
-       clocksource_register_hz(&pit_clk, pit_rate);
-
-       /* Set up irq handler */
-       ret = setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq);
-       if (ret)
-               pr_crit("AT91: PIT: Unable to setup IRQ\n");
-
-       /* Set up and register clockevents */
-       pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift);
-       pit_clkevt.cpumask = cpumask_of(0);
-       clockevents_register_device(&pit_clkevt);
-}
-
-void __init at91sam926x_ioremap_pit(u32 addr)
-{
-#if defined(CONFIG_OF)
-       struct device_node *np =
-               of_find_matching_node(NULL, pit_timer_ids);
-
-       if (np) {
-               of_node_put(np);
-               return;
-       }
-#endif
-       pit_base_addr = ioremap(addr, 16);
-
-       if (!pit_base_addr)
-               panic("Impossible to ioremap PIT\n");
-}
index 7a32970..405427e 100644 (file)
@@ -477,6 +477,11 @@ static unsigned int at91sam9g45_default_irq_priority[NR_AIC_IRQS] __initdata = {
        0,      /* Advanced Interrupt Controller (IRQ0) */
 };
 
+static void __init at91sam9g45_init_time(void)
+{
+       at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
+}
+
 AT91_SOC_START(at91sam9g45)
        .map_io = at91sam9g45_map_io,
        .default_irq_priority = at91sam9g45_default_irq_priority,
@@ -485,4 +490,5 @@ AT91_SOC_START(at91sam9g45)
        .register_clocks = at91sam9g45_register_clocks,
        .register_devices = at91sam9g45_register_devices,
        .init = at91sam9g45_initialize,
+       .init_time = at91sam9g45_init_time,
 AT91_SOC_END
index 264999f..f553e4e 100644 (file)
@@ -410,6 +410,11 @@ static unsigned int at91sam9rl_default_irq_priority[NR_AIC_IRQS] __initdata = {
        0,      /* Advanced Interrupt Controller */
 };
 
+static void __init at91sam9rl_init_time(void)
+{
+       at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS);
+}
+
 AT91_SOC_START(at91sam9rl)
        .map_io = at91sam9rl_map_io,
        .default_irq_priority = at91sam9rl_default_irq_priority,
@@ -420,4 +425,5 @@ AT91_SOC_START(at91sam9rl)
 #endif
        .register_devices = at91sam9rl_register_devices,
        .init = at91sam9rl_initialize,
+       .init_time = at91sam9rl_init_time,
 AT91_SOC_END
index fc9621c..e76e35c 100644 (file)
@@ -213,7 +213,7 @@ static void __init afeb9260_board_init(void)
 
 MACHINE_START(AFEB9260, "Custom afeb9260 board")
        /* Maintainer: Sergey Lapin <slapin@ossfans.org> */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = afeb9260_init_early,
index 283655b..ae827dd 100644 (file)
@@ -190,7 +190,7 @@ static void __init cam60_board_init(void)
 
 MACHINE_START(CAM60, "KwikByte CAM60")
        /* Maintainer: KwikByte */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = cam60_init_early,
index 29a8903..731c831 100644 (file)
@@ -377,7 +377,7 @@ MACHINE_START(CPUAT9260, "Eukrea CPU9260")
 MACHINE_START(CPUAT9G20, "Eukrea CPU9G20")
 #endif
        /* Maintainer: Eric Benard - EUKREA Electromatique */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = cpu9krea_init_early,
index dfa8d48..d3048cc 100644 (file)
 #include "board.h"
 #include "generic.h"
 
-
-static void __init sam9_dt_timer_init(void)
-{
-#if defined(CONFIG_COMMON_CLK)
-       of_clk_init(NULL);
-#endif
-       at91sam926x_pit_init();
-}
-
 static const char *at91_dt_board_compat[] __initdata = {
        "atmel,at91sam9",
        NULL
@@ -41,7 +32,6 @@ static const char *at91_dt_board_compat[] __initdata = {
 
 DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM (Device Tree)")
        /* Maintainer: Atmel */
-       .init_time      = sam9_dt_timer_init,
        .map_io         = at91_map_io,
        .init_early     = at91_dt_initialize,
        .dt_compat      = at91_dt_board_compat,
index d6fe04b..be6aa2e 100644 (file)
 #include "at91_aic.h"
 #include "generic.h"
 
-static void __init sama5_dt_timer_init(void)
-{
-#if defined(CONFIG_COMMON_CLK)
-       of_clk_init(NULL);
-#endif
-       at91sam926x_pit_init();
-}
-
 static int ksz9021rn_phy_fixup(struct phy_device *phy)
 {
        int value;
@@ -69,7 +61,6 @@ static const char *sama5_dt_board_compat[] __initdata = {
 
 DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)")
        /* Maintainer: Atmel */
-       .init_time      = sama5_dt_timer_init,
        .map_io         = at91_map_io,
        .init_early     = at91_dt_initialize,
        .init_machine   = sama5_dt_device_init,
index 79bd411..a6aa4a2 100644 (file)
@@ -162,7 +162,7 @@ static void __init flexibity_board_init(void)
 
 MACHINE_START(FLEXIBITY, "Flexibity Connect")
        /* Maintainer: Maxim Osipov */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = flexibity_init_early,
index 8b22c60..ec290b6 100644 (file)
@@ -263,7 +263,7 @@ static void __init foxg20_board_init(void)
 
 MACHINE_START(ACMENETUSFOXG20, "Acme Systems srl FOX Board G20")
        /* Maintainer: Sergio Tanzilli */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = foxg20_init_early,
index b729dd1..bf5cc55 100644 (file)
@@ -576,7 +576,7 @@ static void __init gsia18s_board_init(void)
 }
 
 MACHINE_START(GSIA18S, "GS_IA18_S")
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = gsia18s_init_early,
index b48d95e..9c26b94 100644 (file)
@@ -219,7 +219,7 @@ static void __init pcontrol_g20_board_init(void)
 
 MACHINE_START(PCONTROL_G20, "PControl G20")
        /* Maintainer: pgsellmann@portner-elektronik.at */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = pcontrol_g20_init_early,
index 7030940..c2166e3 100644 (file)
@@ -221,7 +221,7 @@ static void __init ek_board_init(void)
 
 MACHINE_START(SAM9_L9260, "Olimex SAM9-L9260")
        /* Maintainer: Olimex */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
index 18f49c9..bf8a946 100644 (file)
@@ -345,7 +345,7 @@ static void __init ek_board_init(void)
 
 MACHINE_START(AT91SAM9260EK, "Atmel AT91SAM9260-EK")
        /* Maintainer: Atmel */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
index 5a23e72..e85ada8 100644 (file)
@@ -604,7 +604,7 @@ static void __init ek_board_init(void)
 
 MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK")
        /* Maintainer: Atmel */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
@@ -614,7 +614,7 @@ MACHINE_END
 
 MACHINE_START(AT91SAM9G10EK, "Atmel AT91SAM9G10-EK")
        /* Maintainer: Atmel */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
index e868565..d76680f 100644 (file)
@@ -484,7 +484,7 @@ static void __init ek_board_init(void)
 
 MACHINE_START(AT91SAM9263EK, "Atmel AT91SAM9263-EK")
        /* Maintainer: Atmel */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
index e1be6e2..49f0752 100644 (file)
@@ -410,7 +410,7 @@ static void __init ek_board_init(void)
 
 MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK")
        /* Maintainer: Atmel */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
@@ -420,7 +420,7 @@ MACHINE_END
 
 MACHINE_START(AT91SAM9G20EK_2MMC, "Atmel AT91SAM9G20-EK 2 MMC Slot Mod")
        /* Maintainer: Atmel */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
index 0fc4bea..a517c7f 100644 (file)
@@ -518,7 +518,7 @@ static void __init ek_board_init(void)
 
 MACHINE_START(AT91SAM9M10G45EK, "Atmel AT91SAM9M10G45-EK")
        /* Maintainer: Atmel */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
index 5cb8246..8bca329 100644 (file)
@@ -324,7 +324,7 @@ static void __init ek_board_init(void)
 
 MACHINE_START(AT91SAM9RLEK, "Atmel AT91SAM9RL-EK")
        /* Maintainer: Atmel */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = ek_init_early,
index 2a817b8..b4aff84 100644 (file)
@@ -180,7 +180,7 @@ static void __init snapper9260_board_init(void)
 }
 
 MACHINE_START(SNAPPER_9260, "Bluewater Systems Snapper 9260/9G20 module")
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = snapper9260_init_early,
index 3b57503..e825641 100644 (file)
@@ -275,7 +275,7 @@ static void __init stamp9g20evb_board_init(void)
 
 MACHINE_START(PORTUXG20, "taskit PortuxG20")
        /* Maintainer: taskit GmbH */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = stamp9g20_init_early,
@@ -285,7 +285,7 @@ MACHINE_END
 
 MACHINE_START(STAMP9G20, "taskit Stamp9G20")
        /* Maintainer: taskit GmbH */
-       .init_time      = at91sam926x_pit_init,
+       .init_time      = at91_init_time,
        .map_io         = at91_map_io,
        .handle_irq     = at91_aic_handle_irq,
        .init_early     = stamp9g20_init_early,
index f42b049..189d7c7 100644 (file)
@@ -8,6 +8,9 @@
  * published by the Free Software Foundation.
  */
 
+#ifndef _AT91_GENERIC_H
+#define _AT91_GENERIC_H
+
 #include <linux/clkdev.h>
 #include <linux/of.h>
 #include <linux/reboot.h>
@@ -41,10 +44,11 @@ extern void __init at91_sysirq_mask_rtt(u32 rtt_base);
 extern void __init at91_register_devices(void);
 
  /* Timer */
+extern void __init at91_init_time(void);
 extern void at91rm9200_ioremap_st(u32 addr);
 extern void at91rm9200_timer_init(void);
 extern void at91sam926x_ioremap_pit(u32 addr);
-extern void at91sam926x_pit_init(void);
+extern void at91sam926x_pit_init(int irq);
 extern void at91x40_timer_init(void);
 
  /* Clocks */
@@ -84,3 +88,5 @@ extern int  __init at91_gpio_of_irq_setup(struct device_node *node,
                                          struct device_node *parent);
 
 extern u32 at91_get_extern_irq(void);
+
+#endif /* _AT91_GENERIC_H */
index d2cade2..51c22a2 100644 (file)
@@ -416,3 +416,8 @@ void __init at91_register_devices(void)
 {
        at91_boot_soc.register_devices();
 }
+
+void __init at91_init_time(void)
+{
+       at91_boot_soc.init_time();
+}
index ab983f2..2886b83 100644 (file)
@@ -13,6 +13,7 @@ struct at91_init_soc {
        void (*register_clocks)(void);
        void (*register_devices)(void);
        void (*init)(void);
+       void (*init_time)(void);
 };
 
 extern struct at91_init_soc at91_boot_soc;
index cfd6519..82a2ebe 100644 (file)
@@ -120,6 +120,10 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
        help
         Use ARM global timer clock source as sched_clock
 
+config ATMEL_PIT
+       select CLKSRC_OF if OF
+       def_bool SOC_AT91SAM9 || SOC_SAMA5
+
 config CLKSRC_METAG_GENERIC
        def_bool y if METAG
        help
index 7fd9fd1..e566f6c 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_CLKSRC_OF)        += clksrc-of.o
+obj-$(CONFIG_ATMEL_PIT)                += timer-atmel-pit.o
 obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
 obj-$(CONFIG_X86_PM_TIMER)     += acpi_pm.o
 obj-$(CONFIG_SCx200HR_TIMER)   += scx200_hrt.o
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
new file mode 100644 (file)
index 0000000..d528909
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x
+ *
+ * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France
+ * Revision     2005 M. Nicolas Diremdjian, ATMEL Rousset, France
+ * Converted to ClockSource/ClockEvents by David Brownell.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt)    "AT91: PIT: " fmt
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+
+#define AT91_PIT_MR            0x00                    /* Mode Register */
+#define AT91_PIT_PITIEN                        BIT(25)                 /* Timer Interrupt Enable */
+#define AT91_PIT_PITEN                 BIT(24)                 /* Timer Enabled */
+#define AT91_PIT_PIV                   GENMASK(19, 0)          /* Periodic Interval Value */
+
+#define AT91_PIT_SR            0x04                    /* Status Register */
+#define AT91_PIT_PITS                  BIT(0)                  /* Timer Status */
+
+#define AT91_PIT_PIVR          0x08                    /* Periodic Interval Value Register */
+#define AT91_PIT_PIIR          0x0c                    /* Periodic Interval Image Register */
+#define AT91_PIT_PICNT                 GENMASK(31, 20)         /* Interval Counter */
+#define AT91_PIT_CPIV                  GENMASK(19, 0)          /* Inverval Value */
+
+#define PIT_CPIV(x)    ((x) & AT91_PIT_CPIV)
+#define PIT_PICNT(x)   (((x) & AT91_PIT_PICNT) >> 20)
+
+struct pit_data {
+       struct clock_event_device       clkevt;
+       struct clocksource              clksrc;
+
+       void __iomem    *base;
+       u32             cycle;
+       u32             cnt;
+       unsigned int    irq;
+       struct clk      *mck;
+};
+
+static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc)
+{
+       return container_of(clksrc, struct pit_data, clksrc);
+}
+
+static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt)
+{
+       return container_of(clkevt, struct pit_data, clkevt);
+}
+
+static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset)
+{
+       return __raw_readl(base + reg_offset);
+}
+
+static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value)
+{
+       __raw_writel(value, base + reg_offset);
+}
+
+/*
+ * Clocksource:  just a monotonic counter of MCK/16 cycles.
+ * We don't care whether or not PIT irqs are enabled.
+ */
+static cycle_t read_pit_clk(struct clocksource *cs)
+{
+       struct pit_data *data = clksrc_to_pit_data(cs);
+       unsigned long flags;
+       u32 elapsed;
+       u32 t;
+
+       raw_local_irq_save(flags);
+       elapsed = data->cnt;
+       t = pit_read(data->base, AT91_PIT_PIIR);
+       raw_local_irq_restore(flags);
+
+       elapsed += PIT_PICNT(t) * data->cycle;
+       elapsed += PIT_CPIV(t);
+       return elapsed;
+}
+
+/*
+ * Clockevent device:  interrupts every 1/HZ (== pit_cycles * MCK/16)
+ */
+static void
+pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
+{
+       struct pit_data *data = clkevt_to_pit_data(dev);
+
+       switch (mode) {
+       case CLOCK_EVT_MODE_PERIODIC:
+               /* update clocksource counter */
+               data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
+               pit_write(data->base, AT91_PIT_MR,
+                         (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN);
+               break;
+       case CLOCK_EVT_MODE_ONESHOT:
+               BUG();
+               /* FALLTHROUGH */
+       case CLOCK_EVT_MODE_SHUTDOWN:
+       case CLOCK_EVT_MODE_UNUSED:
+               /* disable irq, leaving the clocksource active */
+               pit_write(data->base, AT91_PIT_MR,
+                         (data->cycle - 1) | AT91_PIT_PITEN);
+               break;
+       case CLOCK_EVT_MODE_RESUME:
+               break;
+       }
+}
+
+static void at91sam926x_pit_suspend(struct clock_event_device *cedev)
+{
+       struct pit_data *data = clkevt_to_pit_data(cedev);
+
+       /* Disable timer */
+       pit_write(data->base, AT91_PIT_MR, 0);
+}
+
+static void at91sam926x_pit_reset(struct pit_data *data)
+{
+       /* Disable timer and irqs */
+       pit_write(data->base, AT91_PIT_MR, 0);
+
+       /* Clear any pending interrupts, wait for PIT to stop counting */
+       while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0)
+               cpu_relax();
+
+       /* Start PIT but don't enable IRQ */
+       pit_write(data->base, AT91_PIT_MR,
+                 (data->cycle - 1) | AT91_PIT_PITEN);
+}
+
+static void at91sam926x_pit_resume(struct clock_event_device *cedev)
+{
+       struct pit_data *data = clkevt_to_pit_data(cedev);
+
+       at91sam926x_pit_reset(data);
+}
+
+/*
+ * IRQ handler for the timer.
+ */
+static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
+{
+       struct pit_data *data = dev_id;
+
+       /*
+        * irqs should be disabled here, but as the irq is shared they are only
+        * guaranteed to be off if the timer irq is registered first.
+        */
+       WARN_ON_ONCE(!irqs_disabled());
+
+       /* The PIT interrupt may be disabled, and is shared */
+       if ((data->clkevt.mode == CLOCK_EVT_MODE_PERIODIC) &&
+           (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) {
+               unsigned nr_ticks;
+
+               /* Get number of ticks performed before irq, and ack it */
+               nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
+               do {
+                       data->cnt += data->cycle;
+                       data->clkevt.event_handler(&data->clkevt);
+                       nr_ticks--;
+               } while (nr_ticks);
+
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+/*
+ * Set up both clocksource and clockevent support.
+ */
+static void __init at91sam926x_pit_common_init(struct pit_data *data)
+{
+       unsigned long   pit_rate;
+       unsigned        bits;
+       int             ret;
+
+       /*
+        * Use our actual MCK to figure out how many MCK/16 ticks per
+        * 1/HZ period (instead of a compile-time constant LATCH).
+        */
+       pit_rate = clk_get_rate(data->mck) / 16;
+       data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ);
+       WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0);
+
+       /* Initialize and enable the timer */
+       at91sam926x_pit_reset(data);
+
+       /*
+        * Register clocksource.  The high order bits of PIV are unused,
+        * so this isn't a 32-bit counter unless we get clockevent irqs.
+        */
+       bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */;
+       data->clksrc.mask = CLOCKSOURCE_MASK(bits);
+       data->clksrc.name = "pit";
+       data->clksrc.rating = 175;
+       data->clksrc.read = read_pit_clk,
+       data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+       clocksource_register_hz(&data->clksrc, pit_rate);
+
+       /* Set up irq handler */
+       ret = request_irq(data->irq, at91sam926x_pit_interrupt,
+                         IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL,
+                         "at91_tick", data);
+       if (ret)
+               panic(pr_fmt("Unable to setup IRQ\n"));
+
+       /* Set up and register clockevents */
+       data->clkevt.name = "pit";
+       data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
+       data->clkevt.shift = 32;
+       data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift);
+       data->clkevt.rating = 100;
+       data->clkevt.cpumask = cpumask_of(0);
+
+       data->clkevt.set_mode = pit_clkevt_mode;
+       data->clkevt.resume = at91sam926x_pit_resume;
+       data->clkevt.suspend = at91sam926x_pit_suspend;
+       clockevents_register_device(&data->clkevt);
+}
+
+static void __init at91sam926x_pit_dt_init(struct device_node *node)
+{
+       struct pit_data *data;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               panic(pr_fmt("Unable to allocate memory\n"));
+
+       data->base = of_iomap(node, 0);
+       if (!data->base)
+               panic(pr_fmt("Could not map PIT address\n"));
+
+       data->mck = of_clk_get(node, 0);
+       if (IS_ERR(data->mck))
+               /* Fallback on clkdev for !CCF-based boards */
+               data->mck = clk_get(NULL, "mck");
+
+       if (IS_ERR(data->mck))
+               panic(pr_fmt("Unable to get mck clk\n"));
+
+       /* Get the interrupts property */
+       data->irq = irq_of_parse_and_map(node, 0);
+       if (!data->irq)
+               panic(pr_fmt("Unable to get IRQ from DT\n"));
+
+       at91sam926x_pit_common_init(data);
+}
+CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
+                      at91sam926x_pit_dt_init);
+
+static void __iomem *pit_base_addr;
+
+void __init at91sam926x_pit_init(int irq)
+{
+       struct pit_data *data;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               panic(pr_fmt("Unable to allocate memory\n"));
+
+       data->base = pit_base_addr;
+
+       data->mck = clk_get(NULL, "mck");
+       if (IS_ERR(data->mck))
+               panic(pr_fmt("Unable to get mck clk\n"));
+
+       data->irq = irq;
+
+       at91sam926x_pit_common_init(data);
+}
+
+void __init at91sam926x_ioremap_pit(u32 addr)
+{
+       if (of_have_populated_dt())
+               return;
+
+       pit_base_addr = ioremap(addr, 16);
+
+       if (!pit_base_addr)
+               panic(pr_fmt("Impossible to ioremap PIT\n"));
+}