diff options
Diffstat (limited to 'arch/arm/mach-at91/at91sam926x_time.c')
-rw-r--r-- | arch/arm/mach-at91/at91sam926x_time.c | 296 |
1 files changed, 0 insertions, 296 deletions
diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c deleted file mode 100644 index d5289098b3df..000000000000 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ /dev/null @@ -1,296 +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. - */ - -#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")); -} |