/* * The High Precision Event Timer driver. * This driver is closely modelled after the rtc.c driver. * See HPET spec revision 1.
*/ #define HPET_USER_FREQ (64) #define HPET_DRIFT (500)
#define HPET_RANGE_SIZE 1024 /* from HPET spec */
/* WARNING -- don't get confused. These macros are never used * to write the (single) counter, and rarely to read it. * They're badly named; to fix, someday.
*/ #if BITS_PER_LONG == 64 #define write_counter(V, MC) writeq(V, MC) #define read_counter(MC) readq(MC) #else #define write_counter(V, MC) writel(V, MC) #define read_counter(MC) readl(MC) #endif
if ((devp->hd_flags & HPET_SHARED_IRQ) &&
!(isr & readl(&devp->hd_hpet->hpet_isr))) return IRQ_NONE;
spin_lock(&hpet_lock);
devp->hd_irqdata++;
/* * For non-periodic timers, increment the accumulator. * This has the effect of treating non-periodic like periodic.
*/ if ((devp->hd_flags & (HPET_IE | HPET_PERIODIC)) == HPET_IE) { unsignedlong t, mc, base, k; struct hpet __iomem *hpet = devp->hd_hpet; struct hpets *hpetp = devp->hd_hpets;
t = devp->hd_ireqfreq;
read_counter(&devp->hd_timer->hpet_compare);
mc = read_counter(&hpet->hpet_mc); /* The time for the next interrupt would logically be t + m, * however, if we are very unlucky and the interrupt is delayed * for longer than t then we will completely miss the next * interrupt if we set t + m and an application will hang. * Therefore we need to make a more complex computation assuming * that there exists a k for which the following is true: * k * t + base < mc + delta * (k + 1) * t + base > mc + delta * where t is the interval in hpet ticks for the given freq, * base is the theoretical start value 0 < base < t, * mc is the main counter value at the time of the interrupt, * delta is the time it takes to write the a value to the * comparator. * k may then be computed as (mc - base + delta) / t .
*/
base = mc % t;
k = (mc - base + hpetp->hp_delta) / t;
write_counter(t * (k + 1) + base,
&devp->hd_timer->hpet_compare);
}
if (devp->hd_flags & HPET_SHARED_IRQ)
writel(isr, &devp->hd_hpet->hpet_isr);
spin_unlock(&hpet_lock);
spin_lock_irq(&hpet_lock); if (devp->hd_hdwirq) {
spin_unlock_irq(&hpet_lock); return;
}
timer = devp->hd_timer;
/* we prefer level triggered mode */
v = readl(&timer->hpet_config); if (!(v & Tn_INT_TYPE_CNF_MASK)) {
v |= Tn_INT_TYPE_CNF_MASK;
writel(v, &timer->hpet_config);
}
spin_unlock_irq(&hpet_lock);
v = (readq(&timer->hpet_config) & Tn_INT_ROUTE_CAP_MASK) >>
Tn_INT_ROUTE_CAP_SHIFT;
/* * In PIC mode, skip IRQ0-4, IRQ6-9, IRQ12-15 which is always used by * legacy device. In IO APIC mode, we skip all the legacy IRQS.
*/ if (acpi_irq_model == ACPI_IRQ_MODEL_PIC)
v &= ~0xf3df; else
v &= ~0xffff;
if (devp->hd_flags & HPET_IE) {
spin_unlock_irq(&hpet_lock); return -EBUSY;
}
devp->hd_flags |= HPET_IE;
if (readl(&timer->hpet_config) & Tn_INT_TYPE_CNF_MASK)
devp->hd_flags |= HPET_SHARED_IRQ;
spin_unlock_irq(&hpet_lock);
irq = devp->hd_hdwirq;
if (irq) { unsignedlong irq_flags;
if (devp->hd_flags & HPET_SHARED_IRQ) { /* * To prevent the interrupt handler from seeing an * unwanted interrupt status bit, program the timer * so that it will not fire in the near future ...
*/
writel(readl(&timer->hpet_config) & ~Tn_TYPE_CNF_MASK,
&timer->hpet_config);
write_counter(read_counter(&hpet->hpet_mc),
&timer->hpet_compare); /* ... and clear any left-over status. */
isr = 1 << (devp - devp->hd_hpets->hp_dev);
writel(isr, &hpet->hpet_isr);
}
devp->hd_irq = irq;
t = devp->hd_ireqfreq;
v = readq(&timer->hpet_config);
/* 64-bit comparators are not yet supported through the ioctls, * so force this into 32-bit mode if it supports both modes
*/
g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK;
if (devp->hd_flags & HPET_PERIODIC) {
g |= Tn_TYPE_CNF_MASK;
v |= Tn_TYPE_CNF_MASK | Tn_VAL_SET_CNF_MASK;
writeq(v, &timer->hpet_config);
local_irq_save(flags);
/* * NOTE: First we modify the hidden accumulator * register supported by periodic-capable comparators. * We never want to modify the (single) counter; that * would affect all the comparators. The value written * is the counter value when the first interrupt is due.
*/
m = read_counter(&hpet->hpet_mc);
write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); /* * Then we modify the comparator, indicating the period * for subsequent interrupt.
*/
write_counter(t, &timer->hpet_compare);
} else {
local_irq_save(flags);
m = read_counter(&hpet->hpet_mc);
write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare);
}
switch (cmd) { case HPET_IE_OFF: case HPET_INFO: case HPET_EPI: case HPET_DPI: case HPET_IRQFREQ:
timer = devp->hd_timer;
hpetp = devp->hd_hpets; break; case HPET_IE_ON: return hpet_ioctl_ieon(devp); default: return -EINVAL;
}
err = 0;
switch (cmd) { case HPET_IE_OFF: if ((devp->hd_flags & HPET_IE) == 0) break;
v = readq(&timer->hpet_config);
v &= ~Tn_INT_ENB_CNF_MASK;
writeq(v, &timer->hpet_config); if (devp->hd_irq) {
free_irq(devp->hd_irq, devp);
devp->hd_irq = 0;
}
devp->hd_flags ^= HPET_IE; break; case HPET_INFO:
{
memset(info, 0, sizeof(*info)); if (devp->hd_ireqfreq)
info->hi_ireqfreq =
hpet_time_div(hpetp, devp->hd_ireqfreq);
info->hi_flags =
readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK;
info->hi_hpet = hpetp->hp_which;
info->hi_timer = devp - hpetp->hp_dev; break;
} case HPET_EPI:
v = readq(&timer->hpet_config); if ((v & Tn_PER_INT_CAP_MASK) == 0) {
err = -ENXIO; break;
}
devp->hd_flags |= HPET_PERIODIC; break; case HPET_DPI:
v = readq(&timer->hpet_config); if ((v & Tn_PER_INT_CAP_MASK) == 0) {
err = -ENXIO; break;
} if (devp->hd_flags & HPET_PERIODIC &&
readq(&timer->hpet_config) & Tn_TYPE_CNF_MASK) {
v = readq(&timer->hpet_config);
v ^= Tn_TYPE_CNF_MASK;
writeq(v, &timer->hpet_config);
}
devp->hd_flags &= ~HPET_PERIODIC; break; case HPET_IRQFREQ: if ((arg > hpet_max_freq) &&
!capable(CAP_SYS_RESOURCE)) {
err = -EACCES; break;
}
/* 32-bit types would lead to different command codes which should be * translated into 64-bit ones before passed to hpet_ioctl_common
*/ #define COMPAT_HPET_INFO _IOR('h', 0x03, struct compat_hpet_info) #define COMPAT_HPET_IRQFREQ _IOW('h', 0x6, compat_ulong_t)
/* * Adjustment for when arming the timer with * initial conditions. That is, main counter * ticks expired before interrupts are enabled.
*/ #define TICK_CALIBRATE (1000UL)
staticunsignedlong __hpet_calibrate(struct hpets *hpetp)
{ struct hpet_timer __iomem *timer = NULL; unsignedlong t, m, count, i, flags, start; struct hpet_dev *devp; int j; struct hpet __iomem *hpet;
/* * Try to calibrate until return value becomes stable small value. * If SMI interruption occurs in calibration loop, the return value * will be big. This avoids its impact.
*/ for ( ; ; ) {
tmp = __hpet_calibrate(hpetp); if (ret <= tmp) break;
ret = tmp;
}
/* * hpet_alloc can be called by platform dependent code. * If platform dependent code has allocated the hpet that * ACPI has also reported, then we catch it here.
*/ if (hpet_is_known(hdp)) {
printk(KERN_DEBUG "%s: duplicate HPET ignored\n",
__func__); return 0;
}
/* * If the timer was reserved by platform code, * then make timer unavailable for opens.
*/ if (hdp->hd_state & (1 << i)) {
devp->hd_flags = HPET_OPEN; continue;
}
result =
acpi_walk_resources(device->handle, METHOD_NAME__CRS,
hpet_resources, &data);
if (ACPI_FAILURE(result)) return -ENODEV;
if (!data.hd_address || !data.hd_nirqs) { if (data.hd_address)
iounmap(data.hd_address);
printk("%s: no address or irqs in _CRS\n", __func__); return -ENODEV;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.