16
16
#include <linux/sched.h>
17
17
#include <linux/time.h>
18
18
#include <linux/clocksource.h>
19
+ #include <linux/clockchips.h>
19
20
#include <linux/interrupt.h>
20
21
#include <linux/module.h>
21
22
#include <linux/init.h>
@@ -43,16 +44,80 @@ static struct clocksource ccount_clocksource = {
43
44
.mask = CLOCKSOURCE_MASK (32 ),
44
45
};
45
46
47
+ static int ccount_timer_set_next_event (unsigned long delta ,
48
+ struct clock_event_device * dev );
49
+ static void ccount_timer_set_mode (enum clock_event_mode mode ,
50
+ struct clock_event_device * evt );
51
+ static struct ccount_timer_t {
52
+ struct clock_event_device evt ;
53
+ int irq_enabled ;
54
+ } ccount_timer = {
55
+ .evt = {
56
+ .name = "ccount_clockevent" ,
57
+ .features = CLOCK_EVT_FEAT_ONESHOT ,
58
+ .rating = 300 ,
59
+ .set_next_event = ccount_timer_set_next_event ,
60
+ .set_mode = ccount_timer_set_mode ,
61
+ },
62
+ };
63
+
64
+ static int ccount_timer_set_next_event (unsigned long delta ,
65
+ struct clock_event_device * dev )
66
+ {
67
+ unsigned long flags , next ;
68
+ int ret = 0 ;
69
+
70
+ local_irq_save (flags );
71
+ next = get_ccount () + delta ;
72
+ set_linux_timer (next );
73
+ if (next - get_ccount () > delta )
74
+ ret = - ETIME ;
75
+ local_irq_restore (flags );
76
+
77
+ return ret ;
78
+ }
79
+
80
+ static void ccount_timer_set_mode (enum clock_event_mode mode ,
81
+ struct clock_event_device * evt )
82
+ {
83
+ struct ccount_timer_t * timer =
84
+ container_of (evt , struct ccount_timer_t , evt );
85
+
86
+ /*
87
+ * There is no way to disable the timer interrupt at the device level,
88
+ * only at the intenable register itself. Since enable_irq/disable_irq
89
+ * calls are nested, we need to make sure that these calls are
90
+ * balanced.
91
+ */
92
+ switch (mode ) {
93
+ case CLOCK_EVT_MODE_SHUTDOWN :
94
+ case CLOCK_EVT_MODE_UNUSED :
95
+ if (timer -> irq_enabled ) {
96
+ disable_irq (evt -> irq );
97
+ timer -> irq_enabled = 0 ;
98
+ }
99
+ break ;
100
+ case CLOCK_EVT_MODE_RESUME :
101
+ case CLOCK_EVT_MODE_ONESHOT :
102
+ if (!timer -> irq_enabled ) {
103
+ enable_irq (evt -> irq );
104
+ timer -> irq_enabled = 1 ;
105
+ }
106
+ default :
107
+ break ;
108
+ }
109
+ }
110
+
46
111
static irqreturn_t timer_interrupt (int irq , void * dev_id );
47
112
static struct irqaction timer_irqaction = {
48
113
.handler = timer_interrupt ,
49
- .flags = IRQF_DISABLED ,
114
+ .flags = IRQF_TIMER ,
50
115
.name = "timer" ,
116
+ .dev_id = & ccount_timer ,
51
117
};
52
118
53
119
void __init time_init (void )
54
120
{
55
- unsigned int irq ;
56
121
#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
57
122
printk ("Calibrating CPU frequency " );
58
123
platform_calibrate_ccount ();
@@ -61,11 +126,14 @@ void __init time_init(void)
61
126
#endif
62
127
clocksource_register_hz (& ccount_clocksource , CCOUNT_PER_JIFFY * HZ );
63
128
64
- /* Initialize the linux timer interrupt. */
65
-
66
- irq = irq_create_mapping (NULL , LINUX_TIMER_INT );
67
- setup_irq (irq , & timer_irqaction );
68
- set_linux_timer (get_ccount () + CCOUNT_PER_JIFFY );
129
+ ccount_timer .evt .cpumask = cpumask_of (0 );
130
+ ccount_timer .evt .irq = irq_create_mapping (NULL , LINUX_TIMER_INT );
131
+ if (WARN (!ccount_timer .evt .irq , "error: can't map timer irq" ))
132
+ return ;
133
+ clockevents_config_and_register (& ccount_timer .evt , ccount_freq , 0xf ,
134
+ 0xffffffff );
135
+ setup_irq (ccount_timer .evt .irq , & timer_irqaction );
136
+ ccount_timer .irq_enabled = 1 ;
69
137
}
70
138
71
139
/*
@@ -74,36 +142,14 @@ void __init time_init(void)
74
142
75
143
irqreturn_t timer_interrupt (int irq , void * dev_id )
76
144
{
145
+ struct ccount_timer_t * timer = dev_id ;
146
+ struct clock_event_device * evt = & timer -> evt ;
77
147
78
- unsigned long next ;
79
-
80
- next = get_linux_timer ();
81
-
82
- again :
83
- while ((signed long )(get_ccount () - next ) > 0 ) {
84
-
85
- profile_tick (CPU_PROFILING );
86
- #ifndef CONFIG_SMP
87
- update_process_times (user_mode (get_irq_regs ()));
88
- #endif
89
-
90
- xtime_update (1 ); /* Linux handler in kernel/time/timekeeping */
91
-
92
- /* Note that writing CCOMPARE clears the interrupt. */
93
-
94
- next += CCOUNT_PER_JIFFY ;
95
- set_linux_timer (next );
96
- }
148
+ evt -> event_handler (evt );
97
149
98
150
/* Allow platform to do something useful (Wdog). */
99
-
100
151
platform_heartbeat ();
101
152
102
- /* Make sure we didn't miss any tick... */
103
-
104
- if ((signed long )(get_ccount () - next ) > 0 )
105
- goto again ;
106
-
107
153
return IRQ_HANDLED ;
108
154
}
109
155
0 commit comments