diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2011-06-09 09:08:25 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2011-07-01 04:37:14 -0400 |
commit | e6220bdc9485c5ea972f9e0e6d062a05934bb74b (patch) | |
tree | 79d049aed6a68281a272d4abc468b23dfaec8a21 /drivers/clocksource/i8253.c | |
parent | 21f07f4f5718449c85c29827ff6fb0cf35a6c96e (diff) |
i8253: Create common clockevent implementation
arm, mips and x86 implement i8253 based clockevents. All the same code
copied. Create a common implementation in drivers/clocksource/i8253.c.
About time to rename drivers/clocksource/ to something else.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: John Stultz <john.stultz@linaro.org>
Link: http://lkml.kernel.org/r/20110609130621.921710458@linutronix.de
Diffstat (limited to 'drivers/clocksource/i8253.c')
-rw-r--r-- | drivers/clocksource/i8253.c | 101 |
1 files changed, 94 insertions, 7 deletions
diff --git a/drivers/clocksource/i8253.c b/drivers/clocksource/i8253.c index e594f52eb88e..27c49e60b7d6 100644 --- a/drivers/clocksource/i8253.c +++ b/drivers/clocksource/i8253.c | |||
@@ -1,13 +1,14 @@ | |||
1 | /* | 1 | /* |
2 | * i8253 PIT clocksource | 2 | * i8253 PIT clocksource |
3 | */ | 3 | */ |
4 | #include <linux/clocksource.h> | 4 | #include <linux/clockchips.h> |
5 | #include <linux/init.h> | 5 | #include <linux/init.h> |
6 | #include <linux/io.h> | 6 | #include <linux/io.h> |
7 | #include <linux/spinlock.h> | 7 | #include <linux/spinlock.h> |
8 | #include <linux/timex.h> | 8 | #include <linux/timex.h> |
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/i8253.h> | 10 | #include <linux/i8253.h> |
11 | #include <linux/smp.h> | ||
11 | 12 | ||
12 | /* | 13 | /* |
13 | * Protects access to I/O ports | 14 | * Protects access to I/O ports |
@@ -47,15 +48,15 @@ static cycle_t i8253_read(struct clocksource *cs) | |||
47 | * count), it cannot be newer. | 48 | * count), it cannot be newer. |
48 | */ | 49 | */ |
49 | jifs = jiffies; | 50 | jifs = jiffies; |
50 | outb_pit(0x00, PIT_MODE); /* latch the count ASAP */ | 51 | outb_p(0x00, PIT_MODE); /* latch the count ASAP */ |
51 | count = inb_pit(PIT_CH0); /* read the latched count */ | 52 | count = inb_p(PIT_CH0); /* read the latched count */ |
52 | count |= inb_pit(PIT_CH0) << 8; | 53 | count |= inb_p(PIT_CH0) << 8; |
53 | 54 | ||
54 | /* VIA686a test code... reset the latch if count > max + 1 */ | 55 | /* VIA686a test code... reset the latch if count > max + 1 */ |
55 | if (count > LATCH) { | 56 | if (count > LATCH) { |
56 | outb_pit(0x34, PIT_MODE); | 57 | outb_p(0x34, PIT_MODE); |
57 | outb_pit(PIT_LATCH & 0xff, PIT_CH0); | 58 | outb_p(PIT_LATCH & 0xff, PIT_CH0); |
58 | outb_pit(PIT_LATCH >> 8, PIT_CH0); | 59 | outb_p(PIT_LATCH >> 8, PIT_CH0); |
59 | count = PIT_LATCH - 1; | 60 | count = PIT_LATCH - 1; |
60 | } | 61 | } |
61 | 62 | ||
@@ -97,3 +98,89 @@ int __init clocksource_i8253_init(void) | |||
97 | return clocksource_register_hz(&i8253_cs, PIT_TICK_RATE); | 98 | return clocksource_register_hz(&i8253_cs, PIT_TICK_RATE); |
98 | } | 99 | } |
99 | #endif | 100 | #endif |
101 | |||
102 | #ifdef CONFIG_CLKEVT_I8253 | ||
103 | /* | ||
104 | * Initialize the PIT timer. | ||
105 | * | ||
106 | * This is also called after resume to bring the PIT into operation again. | ||
107 | */ | ||
108 | static void init_pit_timer(enum clock_event_mode mode, | ||
109 | struct clock_event_device *evt) | ||
110 | { | ||
111 | raw_spin_lock(&i8253_lock); | ||
112 | |||
113 | switch (mode) { | ||
114 | case CLOCK_EVT_MODE_PERIODIC: | ||
115 | /* binary, mode 2, LSB/MSB, ch 0 */ | ||
116 | outb_p(0x34, PIT_MODE); | ||
117 | outb_p(LATCH & 0xff , PIT_CH0); /* LSB */ | ||
118 | outb_p(LATCH >> 8 , PIT_CH0); /* MSB */ | ||
119 | break; | ||
120 | |||
121 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
122 | case CLOCK_EVT_MODE_UNUSED: | ||
123 | if (evt->mode == CLOCK_EVT_MODE_PERIODIC || | ||
124 | evt->mode == CLOCK_EVT_MODE_ONESHOT) { | ||
125 | outb_p(0x30, PIT_MODE); | ||
126 | outb_p(0, PIT_CH0); | ||
127 | outb_p(0, PIT_CH0); | ||
128 | } | ||
129 | break; | ||
130 | |||
131 | case CLOCK_EVT_MODE_ONESHOT: | ||
132 | /* One shot setup */ | ||
133 | outb_p(0x38, PIT_MODE); | ||
134 | break; | ||
135 | |||
136 | case CLOCK_EVT_MODE_RESUME: | ||
137 | /* Nothing to do here */ | ||
138 | break; | ||
139 | } | ||
140 | raw_spin_unlock(&i8253_lock); | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * Program the next event in oneshot mode | ||
145 | * | ||
146 | * Delta is given in PIT ticks | ||
147 | */ | ||
148 | static int pit_next_event(unsigned long delta, struct clock_event_device *evt) | ||
149 | { | ||
150 | raw_spin_lock(&i8253_lock); | ||
151 | outb_p(delta & 0xff , PIT_CH0); /* LSB */ | ||
152 | outb_p(delta >> 8 , PIT_CH0); /* MSB */ | ||
153 | raw_spin_unlock(&i8253_lock); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * On UP the PIT can serve all of the possible timer functions. On SMP systems | ||
160 | * it can be solely used for the global tick. | ||
161 | */ | ||
162 | struct clock_event_device i8253_clockevent = { | ||
163 | .name = "pit", | ||
164 | .features = CLOCK_EVT_FEAT_PERIODIC, | ||
165 | .set_mode = init_pit_timer, | ||
166 | .set_next_event = pit_next_event, | ||
167 | }; | ||
168 | |||
169 | /* | ||
170 | * Initialize the conversion factor and the min/max deltas of the clock event | ||
171 | * structure and register the clock event source with the framework. | ||
172 | */ | ||
173 | void __init clockevent_i8253_init(bool oneshot) | ||
174 | { | ||
175 | if (oneshot) | ||
176 | i8253_clockevent.features |= CLOCK_EVT_FEAT_ONESHOT; | ||
177 | /* | ||
178 | * Start pit with the boot cpu mask. x86 might make it global | ||
179 | * when it is used as broadcast device later. | ||
180 | */ | ||
181 | i8253_clockevent.cpumask = cpumask_of(smp_processor_id()); | ||
182 | |||
183 | clockevents_config_and_register(&i8253_clockevent, PIT_TICK_RATE, | ||
184 | 0xF, 0x7FFF); | ||
185 | } | ||
186 | #endif | ||