Blame view

arch/arm/mach-pnx4008/time.c 3.52 KB
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * arch/arm/mach-pnx4008/time.c
   *
   * PNX4008 Timers
   *
   * Authors: Vitaly Wool, Dmitry Chigirev, Grigory Tolstolytkin <source@mvista.com>
   *
   * 2005 (c) MontaVista Software, Inc. This file is licensed under
   * the terms of the GNU General Public License version 2. This program
   * is licensed "as is" without any warranty of any kind, whether express
   * or implied.
   */
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
13
14
15
16
17
18
19
20
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/delay.h>
  #include <linux/interrupt.h>
  #include <linux/sched.h>
  #include <linux/spinlock.h>
  #include <linux/module.h>
  #include <linux/kallsyms.h>
5904a7f91   Vitaly Wool   [ARM] 3709/1: pnx...
21
22
23
  #include <linux/time.h>
  #include <linux/timex.h>
  #include <linux/irq.h>
fced80c73   Russell King   [ARM] Convert asm...
24
  #include <linux/io.h>
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
25
26
  
  #include <asm/system.h>
a09e64fbc   Russell King   [ARM] Move includ...
27
  #include <mach/hardware.h>
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
28
  #include <asm/leds.h>
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
29
  #include <asm/mach/time.h>
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
30
  #include <asm/errno.h>
2781681ea   Russell King   ARM: PNX4008: rem...
31
  #include "time.h"
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  /*! Note: all timers are UPCOUNTING */
  
  /*!
   * Returns number of us since last clock interrupt.  Note that interrupts
   * will have been disabled by do_gettimeoffset()
   */
  static unsigned long pnx4008_gettimeoffset(void)
  {
  	u32 ticks_to_match =
  	    __raw_readl(HSTIM_MATCH0) - __raw_readl(HSTIM_COUNTER);
  	u32 elapsed = LATCH - ticks_to_match;
  	return (elapsed * (tick_nsec / 1000)) / LATCH;
  }
  
  /*!
   * IRQ handler for the timer
   */
0cd61b68c   Linus Torvalds   Initial blind fix...
49
  static irqreturn_t pnx4008_timer_interrupt(int irq, void *dev_id)
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
50
51
  {
  	if (__raw_readl(HSTIM_INT) & MATCH0_INT) {
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
52
  		do {
0cd61b68c   Linus Torvalds   Initial blind fix...
53
  			timer_tick();
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  
  			/*
  			 * this algorithm takes care of possible delay
  			 * for this interrupt handling longer than a normal
  			 * timer period
  			 */
  			__raw_writel(__raw_readl(HSTIM_MATCH0) + LATCH,
  				     HSTIM_MATCH0);
  			__raw_writel(MATCH0_INT, HSTIM_INT);	/* clear interrupt */
  
  			/*
  			 * The goal is to keep incrementing HSTIM_MATCH0
  			 * register until HSTIM_MATCH0 indicates time after
  			 * what HSTIM_COUNTER indicates.
  			 */
  		} while ((signed)
  			 (__raw_readl(HSTIM_MATCH0) -
  			  __raw_readl(HSTIM_COUNTER)) < 0);
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
72
73
74
75
76
77
78
  	}
  
  	return IRQ_HANDLED;
  }
  
  static struct irqaction pnx4008_timer_irq = {
  	.name = "PNX4008 Tick Timer",
b30fabada   Bernhard Walle   Add IRQF_IRQPOLL ...
79
  	.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
78818e477   Vitaly Wool   [ARM] 3466/1: [2/...
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  	.handler = pnx4008_timer_interrupt
  };
  
  /*!
   * Set up timer and timer interrupt.
   */
  static __init void pnx4008_setup_timer(void)
  {
  	__raw_writel(RESET_COUNT, MSTIM_CTRL);
  	while (__raw_readl(MSTIM_COUNTER)) ;	/* wait for reset to complete. 100% guarantee event */
  	__raw_writel(0, MSTIM_CTRL);	/* stop the timer */
  	__raw_writel(0, MSTIM_MCTRL);
  
  	__raw_writel(RESET_COUNT, HSTIM_CTRL);
  	while (__raw_readl(HSTIM_COUNTER)) ;	/* wait for reset to complete. 100% guarantee event */
  	__raw_writel(0, HSTIM_CTRL);
  	__raw_writel(0, HSTIM_MCTRL);
  	__raw_writel(0, HSTIM_CCR);
  	__raw_writel(12, HSTIM_PMATCH);	/* scale down to 1 MHZ */
  	__raw_writel(LATCH, HSTIM_MATCH0);
  	__raw_writel(MR0_INT, HSTIM_MCTRL);
  
  	setup_irq(HSTIMER_INT, &pnx4008_timer_irq);
  
  	__raw_writel(COUNT_ENAB | DEBUG_EN, HSTIM_CTRL);	/*start timer, stop when JTAG active */
  }
  
  /* Timer Clock Control in PM register */
  #define TIMCLK_CTRL_REG  IO_ADDRESS((PNX4008_PWRMAN_BASE + 0xBC))
  #define WATCHDOG_CLK_EN                   1
  #define TIMER_CLK_EN                      2	/* HS and MS timers? */
  
  static u32 timclk_ctrl_reg_save;
  
  void pnx4008_timer_suspend(void)
  {
  	timclk_ctrl_reg_save = __raw_readl(TIMCLK_CTRL_REG);
  	__raw_writel(0, TIMCLK_CTRL_REG);	/* disable timers */
  }
  
  void pnx4008_timer_resume(void)
  {
  	__raw_writel(timclk_ctrl_reg_save, TIMCLK_CTRL_REG);	/* enable timers */
  }
  
  struct sys_timer pnx4008_timer = {
  	.init = pnx4008_setup_timer,
  	.offset = pnx4008_gettimeoffset,
  	.suspend = pnx4008_timer_suspend,
  	.resume = pnx4008_timer_resume,
  };