123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- /*
- * 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;
- }
- clk_enable(pclk);
- 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;
- }
|