aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/hpet.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/hpet.c')
-rw-r--r--drivers/char/hpet.c25
1 files changed, 23 insertions, 2 deletions
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 051474c65b7..34d6a1cab8d 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -163,11 +163,32 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
163 * This has the effect of treating non-periodic like periodic. 163 * This has the effect of treating non-periodic like periodic.
164 */ 164 */
165 if ((devp->hd_flags & (HPET_IE | HPET_PERIODIC)) == HPET_IE) { 165 if ((devp->hd_flags & (HPET_IE | HPET_PERIODIC)) == HPET_IE) {
166 unsigned long m, t; 166 unsigned long m, t, mc, base, k;
167 struct hpet __iomem *hpet = devp->hd_hpet;
168 struct hpets *hpetp = devp->hd_hpets;
167 169
168 t = devp->hd_ireqfreq; 170 t = devp->hd_ireqfreq;
169 m = read_counter(&devp->hd_timer->hpet_compare); 171 m = read_counter(&devp->hd_timer->hpet_compare);
170 write_counter(t + m, &devp->hd_timer->hpet_compare); 172 mc = read_counter(&hpet->hpet_mc);
173 /* The time for the next interrupt would logically be t + m,
174 * however, if we are very unlucky and the interrupt is delayed
175 * for longer than t then we will completely miss the next
176 * interrupt if we set t + m and an application will hang.
177 * Therefore we need to make a more complex computation assuming
178 * that there exists a k for which the following is true:
179 * k * t + base < mc + delta
180 * (k + 1) * t + base > mc + delta
181 * where t is the interval in hpet ticks for the given freq,
182 * base is the theoretical start value 0 < base < t,
183 * mc is the main counter value at the time of the interrupt,
184 * delta is the time it takes to write the a value to the
185 * comparator.
186 * k may then be computed as (mc - base + delta) / t .
187 */
188 base = mc % t;
189 k = (mc - base + hpetp->hp_delta) / t;
190 write_counter(t * (k + 1) + base,
191 &devp->hd_timer->hpet_compare);
171 } 192 }
172 193
173 if (devp->hd_flags & HPET_SHARED_IRQ) 194 if (devp->hd_flags & HPET_SHARED_IRQ)