aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/hpet.c
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2005-10-30 18:03:31 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-10-30 20:37:29 -0500
commitba3f213f8a31af953b7e86b1d102c919e9935cd4 (patch)
tree1056de1ccb2af403005aa05d5c52399aa36dd6ba /drivers/char/hpet.c
parent9090e6db87e3bdb2a2c187ebc0f9175a9f9e5c6f (diff)
[PATCH] HPET: make frequency calculations 32 bit safe
On 32-bit architectures, the multiplication in the argument for hpet_time_div() often overflows. In the typical case of a 14.32 MHz timer, this happens when the desired frequency exceeds 61 Hz. To avoid this multiplication, we can precompute and store the hardware timer frequency, instead of the period, in the device structure, which leaves us with a simple division when computing the number of timer ticks. As a side effect, this also removes a theoretical bug where the timer interpolator's frequency would be computed as a 32-bit value even if the HPET frequency is greater than 2^32 Hz (the HPET spec allows up to 10 GHz). Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/hpet.c')
-rw-r--r--drivers/char/hpet.c32
1 files changed, 19 insertions, 13 deletions
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index b619ca5eaf8c..a1eb14e6d9f7 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -78,7 +78,7 @@ struct hpets {
78 struct hpet __iomem *hp_hpet; 78 struct hpet __iomem *hp_hpet;
79 unsigned long hp_hpet_phys; 79 unsigned long hp_hpet_phys;
80 struct time_interpolator *hp_interpolator; 80 struct time_interpolator *hp_interpolator;
81 unsigned long hp_period; 81 unsigned long long hp_tick_freq;
82 unsigned long hp_delta; 82 unsigned long hp_delta;
83 unsigned int hp_ntimer; 83 unsigned int hp_ntimer;
84 unsigned int hp_which; 84 unsigned int hp_which;
@@ -427,12 +427,14 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
427 return 0; 427 return 0;
428} 428}
429 429
430static inline unsigned long hpet_time_div(unsigned long dis) 430/* converts Hz to number of timer ticks */
431static inline unsigned long hpet_time_div(struct hpets *hpets,
432 unsigned long dis)
431{ 433{
432 unsigned long long m = 1000000000000000ULL; 434 unsigned long long m;
433 435
436 m = hpets->hp_tick_freq + (dis >> 1);
434 do_div(m, dis); 437 do_div(m, dis);
435
436 return (unsigned long)m; 438 return (unsigned long)m;
437} 439}
438 440
@@ -480,7 +482,7 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg, int kernel)
480 { 482 {
481 struct hpet_info info; 483 struct hpet_info info;
482 484
483 info.hi_ireqfreq = hpet_time_div(hpetp->hp_period * 485 info.hi_ireqfreq = hpet_time_div(hpetp,
484 devp->hd_ireqfreq); 486 devp->hd_ireqfreq);
485 info.hi_flags = 487 info.hi_flags =
486 readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK; 488 readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK;
@@ -524,7 +526,7 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg, int kernel)
524 break; 526 break;
525 } 527 }
526 528
527 devp->hd_ireqfreq = hpet_time_div(hpetp->hp_period * arg); 529 devp->hd_ireqfreq = hpet_time_div(hpetp, arg);
528 } 530 }
529 531
530 return err; 532 return err;
@@ -713,7 +715,7 @@ static void hpet_register_interpolator(struct hpets *hpetp)
713 ti->source = TIME_SOURCE_MMIO64; 715 ti->source = TIME_SOURCE_MMIO64;
714 ti->shift = 10; 716 ti->shift = 10;
715 ti->addr = &hpetp->hp_hpet->hpet_mc; 717 ti->addr = &hpetp->hp_hpet->hpet_mc;
716 ti->frequency = hpet_time_div(hpets->hp_period); 718 ti->frequency = hpetp->hp_tick_freq;
717 ti->drift = HPET_DRIFT; 719 ti->drift = HPET_DRIFT;
718 ti->mask = -1; 720 ti->mask = -1;
719 721
@@ -750,7 +752,7 @@ static unsigned long hpet_calibrate(struct hpets *hpetp)
750 t = read_counter(&timer->hpet_compare); 752 t = read_counter(&timer->hpet_compare);
751 753
752 i = 0; 754 i = 0;
753 count = hpet_time_div(hpetp->hp_period * TICK_CALIBRATE); 755 count = hpet_time_div(hpetp, TICK_CALIBRATE);
754 756
755 local_irq_save(flags); 757 local_irq_save(flags);
756 758
@@ -775,7 +777,8 @@ int hpet_alloc(struct hpet_data *hdp)
775 size_t siz; 777 size_t siz;
776 struct hpet __iomem *hpet; 778 struct hpet __iomem *hpet;
777 static struct hpets *last = (struct hpets *)0; 779 static struct hpets *last = (struct hpets *)0;
778 unsigned long ns; 780 unsigned long ns, period;
781 unsigned long long temp;
779 782
780 /* 783 /*
781 * hpet_alloc can be called by platform dependent code. 784 * hpet_alloc can be called by platform dependent code.
@@ -825,8 +828,12 @@ int hpet_alloc(struct hpet_data *hdp)
825 828
826 last = hpetp; 829 last = hpetp;
827 830
828 hpetp->hp_period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >> 831 period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>
829 HPET_COUNTER_CLK_PERIOD_SHIFT; 832 HPET_COUNTER_CLK_PERIOD_SHIFT; /* fs, 10^-15 */
833 temp = 1000000000000000uLL; /* 10^15 femtoseconds per second */
834 temp += period >> 1; /* round */
835 do_div(temp, period);
836 hpetp->hp_tick_freq = temp; /* ticks per second */
830 837
831 printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s", 838 printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s",
832 hpetp->hp_which, hdp->hd_phys_address, 839 hpetp->hp_which, hdp->hd_phys_address,
@@ -835,8 +842,7 @@ int hpet_alloc(struct hpet_data *hdp)
835 printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); 842 printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
836 printk("\n"); 843 printk("\n");
837 844
838 ns = hpetp->hp_period; /* femptoseconds, 10^-15 */ 845 ns = period / 1000000; /* convert to nanoseconds, 10^-9 */
839 ns /= 1000000; /* convert to nanoseconds, 10^-9 */
840 printk(KERN_INFO "hpet%d: %ldns tick, %d %d-bit timers\n", 846 printk(KERN_INFO "hpet%d: %ldns tick, %d %d-bit timers\n",
841 hpetp->hp_which, ns, hpetp->hp_ntimer, 847 hpetp->hp_which, ns, hpetp->hp_ntimer,
842 cap & HPET_COUNTER_SIZE_MASK ? 64 : 32); 848 cap & HPET_COUNTER_SIZE_MASK ? 64 : 32);