root/arch/arm/plat-orion/time.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. orion_read_sched_clock
  2. orion_clkevt_next_event
  3. orion_clkevt_shutdown
  4. orion_clkevt_set_periodic
  5. orion_timer_interrupt
  6. orion_time_set_base
  7. orion_delay_timer_read
  8. orion_time_init

   1 /*
   2  * arch/arm/plat-orion/time.c
   3  *
   4  * Marvell Orion SoC timer handling.
   5  *
   6  * This file is licensed under the terms of the GNU General Public
   7  * License version 2.  This program is licensed "as is" without any
   8  * warranty of any kind, whether express or implied.
   9  *
  10  * Timer 0 is used as free-running clocksource, while timer 1 is
  11  * used as clock_event_device.
  12  */
  13 
  14 #include <linux/kernel.h>
  15 #include <linux/timer.h>
  16 #include <linux/clockchips.h>
  17 #include <linux/interrupt.h>
  18 #include <linux/irq.h>
  19 #include <linux/sched_clock.h>
  20 #include <plat/time.h>
  21 #include <asm/delay.h>
  22 
  23 /*
  24  * MBus bridge block registers.
  25  */
  26 #define BRIDGE_CAUSE_OFF        0x0110
  27 #define BRIDGE_MASK_OFF         0x0114
  28 #define  BRIDGE_INT_TIMER0       0x0002
  29 #define  BRIDGE_INT_TIMER1       0x0004
  30 
  31 
  32 /*
  33  * Timer block registers.
  34  */
  35 #define TIMER_CTRL_OFF          0x0000
  36 #define  TIMER0_EN               0x0001
  37 #define  TIMER0_RELOAD_EN        0x0002
  38 #define  TIMER1_EN               0x0004
  39 #define  TIMER1_RELOAD_EN        0x0008
  40 #define TIMER0_RELOAD_OFF       0x0010
  41 #define TIMER0_VAL_OFF          0x0014
  42 #define TIMER1_RELOAD_OFF       0x0018
  43 #define TIMER1_VAL_OFF          0x001c
  44 
  45 
  46 /*
  47  * SoC-specific data.
  48  */
  49 static void __iomem *bridge_base;
  50 static u32 bridge_timer1_clr_mask;
  51 static void __iomem *timer_base;
  52 
  53 
  54 /*
  55  * Number of timer ticks per jiffy.
  56  */
  57 static u32 ticks_per_jiffy;
  58 
  59 
  60 /*
  61  * Orion's sched_clock implementation. It has a resolution of
  62  * at least 7.5ns (133MHz TCLK).
  63  */
  64 
  65 static u64 notrace orion_read_sched_clock(void)
  66 {
  67         return ~readl(timer_base + TIMER0_VAL_OFF);
  68 }
  69 
  70 /*
  71  * Clockevent handling.
  72  */
  73 static int
  74 orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
  75 {
  76         unsigned long flags;
  77         u32 u;
  78 
  79         if (delta == 0)
  80                 return -ETIME;
  81 
  82         local_irq_save(flags);
  83 
  84         /*
  85          * Clear and enable clockevent timer interrupt.
  86          */
  87         writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
  88 
  89         u = readl(bridge_base + BRIDGE_MASK_OFF);
  90         u |= BRIDGE_INT_TIMER1;
  91         writel(u, bridge_base + BRIDGE_MASK_OFF);
  92 
  93         /*
  94          * Setup new clockevent timer value.
  95          */
  96         writel(delta, timer_base + TIMER1_VAL_OFF);
  97 
  98         /*
  99          * Enable the timer.
 100          */
 101         u = readl(timer_base + TIMER_CTRL_OFF);
 102         u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN;
 103         writel(u, timer_base + TIMER_CTRL_OFF);
 104 
 105         local_irq_restore(flags);
 106 
 107         return 0;
 108 }
 109 
 110 static int orion_clkevt_shutdown(struct clock_event_device *evt)
 111 {
 112         unsigned long flags;
 113         u32 u;
 114 
 115         local_irq_save(flags);
 116 
 117         /* Disable timer */
 118         u = readl(timer_base + TIMER_CTRL_OFF);
 119         writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF);
 120 
 121         /* Disable timer interrupt */
 122         u = readl(bridge_base + BRIDGE_MASK_OFF);
 123         writel(u & ~BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
 124 
 125         /* ACK pending timer interrupt */
 126         writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
 127 
 128         local_irq_restore(flags);
 129 
 130         return 0;
 131 }
 132 
 133 static int orion_clkevt_set_periodic(struct clock_event_device *evt)
 134 {
 135         unsigned long flags;
 136         u32 u;
 137 
 138         local_irq_save(flags);
 139 
 140         /* Setup timer to fire at 1/HZ intervals */
 141         writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF);
 142         writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF);
 143 
 144         /* Enable timer interrupt */
 145         u = readl(bridge_base + BRIDGE_MASK_OFF);
 146         writel(u | BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF);
 147 
 148         /* Enable timer */
 149         u = readl(timer_base + TIMER_CTRL_OFF);
 150         writel(u | TIMER1_EN | TIMER1_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
 151 
 152         local_irq_restore(flags);
 153 
 154         return 0;
 155 }
 156 
 157 static struct clock_event_device orion_clkevt = {
 158         .name                   = "orion_tick",
 159         .features               = CLOCK_EVT_FEAT_ONESHOT |
 160                                   CLOCK_EVT_FEAT_PERIODIC,
 161         .rating                 = 300,
 162         .set_next_event         = orion_clkevt_next_event,
 163         .set_state_shutdown     = orion_clkevt_shutdown,
 164         .set_state_periodic     = orion_clkevt_set_periodic,
 165         .set_state_oneshot      = orion_clkevt_shutdown,
 166         .tick_resume            = orion_clkevt_shutdown,
 167 };
 168 
 169 static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
 170 {
 171         /*
 172          * ACK timer interrupt and call event handler.
 173          */
 174         writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF);
 175         orion_clkevt.event_handler(&orion_clkevt);
 176 
 177         return IRQ_HANDLED;
 178 }
 179 
 180 static struct irqaction orion_timer_irq = {
 181         .name           = "orion_tick",
 182         .flags          = IRQF_TIMER,
 183         .handler        = orion_timer_interrupt
 184 };
 185 
 186 void __init
 187 orion_time_set_base(void __iomem *_timer_base)
 188 {
 189         timer_base = _timer_base;
 190 }
 191 
 192 static unsigned long orion_delay_timer_read(void)
 193 {
 194         return ~readl(timer_base + TIMER0_VAL_OFF);
 195 }
 196 
 197 static struct delay_timer orion_delay_timer = {
 198         .read_current_timer = orion_delay_timer_read,
 199 };
 200 
 201 void __init
 202 orion_time_init(void __iomem *_bridge_base, u32 _bridge_timer1_clr_mask,
 203                 unsigned int irq, unsigned int tclk)
 204 {
 205         u32 u;
 206 
 207         /*
 208          * Set SoC-specific data.
 209          */
 210         bridge_base = _bridge_base;
 211         bridge_timer1_clr_mask = _bridge_timer1_clr_mask;
 212 
 213         ticks_per_jiffy = (tclk + HZ/2) / HZ;
 214 
 215         orion_delay_timer.freq = tclk;
 216         register_current_timer_delay(&orion_delay_timer);
 217 
 218         /*
 219          * Set scale and timer for sched_clock.
 220          */
 221         sched_clock_register(orion_read_sched_clock, 32, tclk);
 222 
 223         /*
 224          * Setup free-running clocksource timer (interrupts
 225          * disabled).
 226          */
 227         writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
 228         writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
 229         u = readl(bridge_base + BRIDGE_MASK_OFF);
 230         writel(u & ~BRIDGE_INT_TIMER0, bridge_base + BRIDGE_MASK_OFF);
 231         u = readl(timer_base + TIMER_CTRL_OFF);
 232         writel(u | TIMER0_EN | TIMER0_RELOAD_EN, timer_base + TIMER_CTRL_OFF);
 233         clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, "orion_clocksource",
 234                 tclk, 300, 32, clocksource_mmio_readl_down);
 235 
 236         /*
 237          * Setup clockevent timer (interrupt-driven).
 238          */
 239         setup_irq(irq, &orion_timer_irq);
 240         orion_clkevt.cpumask = cpumask_of(0);
 241         clockevents_config_and_register(&orion_clkevt, tclk, 1, 0xfffffffe);
 242 }

/* [<][>][^][v][top][bottom][index][help] */