diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-at91/at91sam926x_time.c | 179 |
1 files changed, 108 insertions, 71 deletions
diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index e476474cb05e..9abb289dce72 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -20,6 +20,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/slab.h> #include <mach/hardware.h> @@ -39,19 +40,35 @@ #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; +struct pit_data { + struct clock_event_device clkevt; + struct clocksource clksrc; -static inline unsigned int pit_read(unsigned int reg_offset) + 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 __raw_readl(pit_base_addr + reg_offset); + return container_of(clksrc, struct pit_data, clksrc); } -static inline void pit_write(unsigned int reg_offset, unsigned long value) +static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt) { - __raw_writel(value, pit_base_addr + reg_offset); + 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); } /* @@ -60,40 +77,35 @@ static inline void pit_write(unsigned int reg_offset, unsigned long value) */ 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 = pit_cnt; - t = pit_read(AT91_PIT_PIIR); + elapsed = data->cnt; + t = pit_read(data->base, AT91_PIT_PIIR); raw_local_irq_restore(flags); - elapsed += PIT_PICNT(t) * pit_cycle; + elapsed += PIT_PICNT(t) * data->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) { + struct pit_data *data = clkevt_to_pit_data(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); + 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(); @@ -101,7 +113,8 @@ pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) 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); + pit_write(data->base, AT91_PIT_MR, + (data->cycle - 1) | AT91_PIT_PITEN); break; case CLOCK_EVT_MODE_RESUME: break; @@ -110,44 +123,40 @@ pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) static void at91sam926x_pit_suspend(struct clock_event_device *cedev) { + struct pit_data *data = clkevt_to_pit_data(cedev); + /* Disable timer */ - pit_write(AT91_PIT_MR, 0); + pit_write(data->base, AT91_PIT_MR, 0); } -static void at91sam926x_pit_reset(void) +static void at91sam926x_pit_reset(struct pit_data *data) { /* Disable timer and irqs */ - pit_write(AT91_PIT_MR, 0); + pit_write(data->base, AT91_PIT_MR, 0); /* Clear any pending interrupts, wait for PIT to stop counting */ - while (PIT_CPIV(pit_read(AT91_PIT_PIVR)) != 0) + while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0) cpu_relax(); /* Start PIT but don't enable IRQ */ - pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN); + pit_write(data->base, AT91_PIT_MR, + (data->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, -}; + 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. @@ -155,15 +164,15 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) 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)) { + 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(AT91_PIT_PIVR)); + nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); do { - pit_cnt += pit_cycle; - pit_clkevt.event_handler(&pit_clkevt); + data->cnt += data->cycle; + data->clkevt.event_handler(&data->clkevt); nr_ticks--; } while (nr_ticks); @@ -176,7 +185,7 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) /* * Set up both clocksource and clockevent support. */ -static void __init at91sam926x_pit_common_init(unsigned int pit_irq) +static void __init at91sam926x_pit_common_init(struct pit_data *data) { unsigned long pit_rate; unsigned bits; @@ -186,67 +195,95 @@ static void __init at91sam926x_pit_common_init(unsigned int pit_irq) * 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(mck) / 16; - pit_cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); - WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0); + 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(); + 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(pit_cycle) /* PIV */; - pit_clk.mask = CLOCKSOURCE_MASK(bits); - clocksource_register_hz(&pit_clk, pit_rate); + 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(pit_irq, at91sam926x_pit_interrupt, + ret = request_irq(data->irq, at91sam926x_pit_interrupt, IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, - "at91_tick", pit_base_addr); + "at91_tick", data); if (ret) panic(pr_fmt("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); + 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) { - unsigned int irq; + struct pit_data *data; - pit_base_addr = of_iomap(node, 0); - if (!pit_base_addr) + 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")); - mck = of_clk_get(node, 0); - if (IS_ERR(mck)) + data->mck = of_clk_get(node, 0); + if (IS_ERR(data->mck)) /* Fallback on clkdev for !CCF-based boards */ - mck = clk_get(NULL, "mck"); + data->mck = clk_get(NULL, "mck"); - if (IS_ERR(mck)) + if (IS_ERR(data->mck)) panic(pr_fmt("Unable to get mck clk\n")); /* Get the interrupts property */ - irq = irq_of_parse_and_map(node, 0); - if (!irq) + 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(irq); + 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(void) { - mck = clk_get(NULL, "mck"); - if (IS_ERR(mck)) + 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")); - at91sam926x_pit_common_init(NR_IRQS_LEGACY + AT91_ID_SYS); + data->irq = NR_IRQS_LEGACY + AT91_ID_SYS; + + at91sam926x_pit_common_init(data); } void __init at91sam926x_ioremap_pit(u32 addr) |