diff options
Diffstat (limited to 'drivers/char/hpet.c')
-rw-r--r-- | drivers/char/hpet.c | 168 |
1 files changed, 113 insertions, 55 deletions
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index c055bb630ffc..3808d9572619 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c | |||
@@ -49,7 +49,9 @@ | |||
49 | #define HPET_USER_FREQ (64) | 49 | #define HPET_USER_FREQ (64) |
50 | #define HPET_DRIFT (500) | 50 | #define HPET_DRIFT (500) |
51 | 51 | ||
52 | static u32 hpet_ntimer, hpet_nhpet, hpet_max_freq = HPET_USER_FREQ; | 52 | #define HPET_RANGE_SIZE 1024 /* from HPET spec */ |
53 | |||
54 | static u32 hpet_nhpet, hpet_max_freq = HPET_USER_FREQ; | ||
53 | 55 | ||
54 | /* A lock for concurrent access by app and isr hpet activity. */ | 56 | /* A lock for concurrent access by app and isr hpet activity. */ |
55 | static DEFINE_SPINLOCK(hpet_lock); | 57 | static DEFINE_SPINLOCK(hpet_lock); |
@@ -78,7 +80,7 @@ struct hpets { | |||
78 | struct hpet __iomem *hp_hpet; | 80 | struct hpet __iomem *hp_hpet; |
79 | unsigned long hp_hpet_phys; | 81 | unsigned long hp_hpet_phys; |
80 | struct time_interpolator *hp_interpolator; | 82 | struct time_interpolator *hp_interpolator; |
81 | unsigned long hp_period; | 83 | unsigned long long hp_tick_freq; |
82 | unsigned long hp_delta; | 84 | unsigned long hp_delta; |
83 | unsigned int hp_ntimer; | 85 | unsigned int hp_ntimer; |
84 | unsigned int hp_which; | 86 | unsigned int hp_which; |
@@ -90,6 +92,7 @@ static struct hpets *hpets; | |||
90 | #define HPET_OPEN 0x0001 | 92 | #define HPET_OPEN 0x0001 |
91 | #define HPET_IE 0x0002 /* interrupt enabled */ | 93 | #define HPET_IE 0x0002 /* interrupt enabled */ |
92 | #define HPET_PERIODIC 0x0004 | 94 | #define HPET_PERIODIC 0x0004 |
95 | #define HPET_SHARED_IRQ 0x0008 | ||
93 | 96 | ||
94 | #if BITS_PER_LONG == 64 | 97 | #if BITS_PER_LONG == 64 |
95 | #define write_counter(V, MC) writeq(V, MC) | 98 | #define write_counter(V, MC) writeq(V, MC) |
@@ -120,6 +123,11 @@ static irqreturn_t hpet_interrupt(int irq, void *data, struct pt_regs *regs) | |||
120 | unsigned long isr; | 123 | unsigned long isr; |
121 | 124 | ||
122 | devp = data; | 125 | devp = data; |
126 | isr = 1 << (devp - devp->hd_hpets->hp_dev); | ||
127 | |||
128 | if ((devp->hd_flags & HPET_SHARED_IRQ) && | ||
129 | !(isr & readl(&devp->hd_hpet->hpet_isr))) | ||
130 | return IRQ_NONE; | ||
123 | 131 | ||
124 | spin_lock(&hpet_lock); | 132 | spin_lock(&hpet_lock); |
125 | devp->hd_irqdata++; | 133 | devp->hd_irqdata++; |
@@ -137,8 +145,8 @@ static irqreturn_t hpet_interrupt(int irq, void *data, struct pt_regs *regs) | |||
137 | &devp->hd_timer->hpet_compare); | 145 | &devp->hd_timer->hpet_compare); |
138 | } | 146 | } |
139 | 147 | ||
140 | isr = (1 << (devp - devp->hd_hpets->hp_dev)); | 148 | if (devp->hd_flags & HPET_SHARED_IRQ) |
141 | writeq(isr, &devp->hd_hpet->hpet_isr); | 149 | writel(isr, &devp->hd_hpet->hpet_isr); |
142 | spin_unlock(&hpet_lock); | 150 | spin_unlock(&hpet_lock); |
143 | 151 | ||
144 | spin_lock(&hpet_task_lock); | 152 | spin_lock(&hpet_task_lock); |
@@ -276,7 +284,8 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma) | |||
276 | 284 | ||
277 | if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, | 285 | if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, |
278 | PAGE_SIZE, vma->vm_page_prot)) { | 286 | PAGE_SIZE, vma->vm_page_prot)) { |
279 | printk(KERN_ERR "remap_pfn_range failed in hpet.c\n"); | 287 | printk(KERN_ERR "%s: io_remap_pfn_range failed\n", |
288 | __FUNCTION__); | ||
280 | return -EAGAIN; | 289 | return -EAGAIN; |
281 | } | 290 | } |
282 | 291 | ||
@@ -364,7 +373,9 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) | |||
364 | hpet = devp->hd_hpet; | 373 | hpet = devp->hd_hpet; |
365 | hpetp = devp->hd_hpets; | 374 | hpetp = devp->hd_hpets; |
366 | 375 | ||
367 | v = readq(&timer->hpet_config); | 376 | if (!devp->hd_ireqfreq) |
377 | return -EIO; | ||
378 | |||
368 | spin_lock_irq(&hpet_lock); | 379 | spin_lock_irq(&hpet_lock); |
369 | 380 | ||
370 | if (devp->hd_flags & HPET_IE) { | 381 | if (devp->hd_flags & HPET_IE) { |
@@ -373,16 +384,21 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) | |||
373 | } | 384 | } |
374 | 385 | ||
375 | devp->hd_flags |= HPET_IE; | 386 | devp->hd_flags |= HPET_IE; |
387 | |||
388 | if (readl(&timer->hpet_config) & Tn_INT_TYPE_CNF_MASK) | ||
389 | devp->hd_flags |= HPET_SHARED_IRQ; | ||
376 | spin_unlock_irq(&hpet_lock); | 390 | spin_unlock_irq(&hpet_lock); |
377 | 391 | ||
378 | t = readq(&timer->hpet_config); | ||
379 | irq = devp->hd_hdwirq; | 392 | irq = devp->hd_hdwirq; |
380 | 393 | ||
381 | if (irq) { | 394 | if (irq) { |
382 | sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev)); | 395 | unsigned long irq_flags; |
383 | 396 | ||
384 | if (request_irq | 397 | sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev)); |
385 | (irq, hpet_interrupt, SA_INTERRUPT, devp->hd_name, (void *)devp)) { | 398 | irq_flags = devp->hd_flags & HPET_SHARED_IRQ |
399 | ? SA_SHIRQ : SA_INTERRUPT; | ||
400 | if (request_irq(irq, hpet_interrupt, irq_flags, | ||
401 | devp->hd_name, (void *)devp)) { | ||
386 | printk(KERN_ERR "hpet: IRQ %d is not free\n", irq); | 402 | printk(KERN_ERR "hpet: IRQ %d is not free\n", irq); |
387 | irq = 0; | 403 | irq = 0; |
388 | } | 404 | } |
@@ -416,20 +432,24 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) | |||
416 | write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); | 432 | write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); |
417 | } | 433 | } |
418 | 434 | ||
419 | isr = (1 << (devp - hpets->hp_dev)); | 435 | if (devp->hd_flags & HPET_SHARED_IRQ) { |
420 | writeq(isr, &hpet->hpet_isr); | 436 | isr = 1 << (devp - devp->hd_hpets->hp_dev); |
437 | writel(isr, &hpet->hpet_isr); | ||
438 | } | ||
421 | writeq(g, &timer->hpet_config); | 439 | writeq(g, &timer->hpet_config); |
422 | local_irq_restore(flags); | 440 | local_irq_restore(flags); |
423 | 441 | ||
424 | return 0; | 442 | return 0; |
425 | } | 443 | } |
426 | 444 | ||
427 | static inline unsigned long hpet_time_div(unsigned long dis) | 445 | /* converts Hz to number of timer ticks */ |
446 | static inline unsigned long hpet_time_div(struct hpets *hpets, | ||
447 | unsigned long dis) | ||
428 | { | 448 | { |
429 | unsigned long long m = 1000000000000000ULL; | 449 | unsigned long long m; |
430 | 450 | ||
451 | m = hpets->hp_tick_freq + (dis >> 1); | ||
431 | do_div(m, dis); | 452 | do_div(m, dis); |
432 | |||
433 | return (unsigned long)m; | 453 | return (unsigned long)m; |
434 | } | 454 | } |
435 | 455 | ||
@@ -477,14 +497,21 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg, int kernel) | |||
477 | { | 497 | { |
478 | struct hpet_info info; | 498 | struct hpet_info info; |
479 | 499 | ||
480 | info.hi_ireqfreq = hpet_time_div(hpetp->hp_period * | 500 | if (devp->hd_ireqfreq) |
481 | devp->hd_ireqfreq); | 501 | info.hi_ireqfreq = |
502 | hpet_time_div(hpetp, devp->hd_ireqfreq); | ||
503 | else | ||
504 | info.hi_ireqfreq = 0; | ||
482 | info.hi_flags = | 505 | info.hi_flags = |
483 | readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK; | 506 | readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK; |
484 | info.hi_hpet = devp->hd_hpets->hp_which; | 507 | info.hi_hpet = hpetp->hp_which; |
485 | info.hi_timer = devp - devp->hd_hpets->hp_dev; | 508 | info.hi_timer = devp - hpetp->hp_dev; |
486 | if (copy_to_user((void __user *)arg, &info, sizeof(info))) | 509 | if (kernel) |
487 | err = -EFAULT; | 510 | memcpy((void *)arg, &info, sizeof(info)); |
511 | else | ||
512 | if (copy_to_user((void __user *)arg, &info, | ||
513 | sizeof(info))) | ||
514 | err = -EFAULT; | ||
488 | break; | 515 | break; |
489 | } | 516 | } |
490 | case HPET_EPI: | 517 | case HPET_EPI: |
@@ -516,12 +543,12 @@ hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg, int kernel) | |||
516 | break; | 543 | break; |
517 | } | 544 | } |
518 | 545 | ||
519 | if (arg & (arg - 1)) { | 546 | if (!arg) { |
520 | err = -EINVAL; | 547 | err = -EINVAL; |
521 | break; | 548 | break; |
522 | } | 549 | } |
523 | 550 | ||
524 | devp->hd_ireqfreq = hpet_time_div(hpetp->hp_period * arg); | 551 | devp->hd_ireqfreq = hpet_time_div(hpetp, arg); |
525 | } | 552 | } |
526 | 553 | ||
527 | return err; | 554 | return err; |
@@ -539,6 +566,17 @@ static struct file_operations hpet_fops = { | |||
539 | .mmap = hpet_mmap, | 566 | .mmap = hpet_mmap, |
540 | }; | 567 | }; |
541 | 568 | ||
569 | static int hpet_is_known(struct hpet_data *hdp) | ||
570 | { | ||
571 | struct hpets *hpetp; | ||
572 | |||
573 | for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next) | ||
574 | if (hpetp->hp_hpet_phys == hdp->hd_phys_address) | ||
575 | return 1; | ||
576 | |||
577 | return 0; | ||
578 | } | ||
579 | |||
542 | EXPORT_SYMBOL(hpet_alloc); | 580 | EXPORT_SYMBOL(hpet_alloc); |
543 | EXPORT_SYMBOL(hpet_register); | 581 | EXPORT_SYMBOL(hpet_register); |
544 | EXPORT_SYMBOL(hpet_unregister); | 582 | EXPORT_SYMBOL(hpet_unregister); |
@@ -563,6 +601,8 @@ int hpet_register(struct hpet_task *tp, int periodic) | |||
563 | return -EINVAL; | 601 | return -EINVAL; |
564 | } | 602 | } |
565 | 603 | ||
604 | tp->ht_opaque = NULL; | ||
605 | |||
566 | spin_lock_irq(&hpet_task_lock); | 606 | spin_lock_irq(&hpet_task_lock); |
567 | spin_lock(&hpet_lock); | 607 | spin_lock(&hpet_lock); |
568 | 608 | ||
@@ -702,15 +742,14 @@ static void hpet_register_interpolator(struct hpets *hpetp) | |||
702 | #ifdef CONFIG_TIME_INTERPOLATION | 742 | #ifdef CONFIG_TIME_INTERPOLATION |
703 | struct time_interpolator *ti; | 743 | struct time_interpolator *ti; |
704 | 744 | ||
705 | ti = kmalloc(sizeof(*ti), GFP_KERNEL); | 745 | ti = kzalloc(sizeof(*ti), GFP_KERNEL); |
706 | if (!ti) | 746 | if (!ti) |
707 | return; | 747 | return; |
708 | 748 | ||
709 | memset(ti, 0, sizeof(*ti)); | ||
710 | ti->source = TIME_SOURCE_MMIO64; | 749 | ti->source = TIME_SOURCE_MMIO64; |
711 | ti->shift = 10; | 750 | ti->shift = 10; |
712 | ti->addr = &hpetp->hp_hpet->hpet_mc; | 751 | ti->addr = &hpetp->hp_hpet->hpet_mc; |
713 | ti->frequency = hpet_time_div(hpets->hp_period); | 752 | ti->frequency = hpetp->hp_tick_freq; |
714 | ti->drift = HPET_DRIFT; | 753 | ti->drift = HPET_DRIFT; |
715 | ti->mask = -1; | 754 | ti->mask = -1; |
716 | 755 | ||
@@ -743,11 +782,11 @@ static unsigned long hpet_calibrate(struct hpets *hpetp) | |||
743 | if (!timer) | 782 | if (!timer) |
744 | return 0; | 783 | return 0; |
745 | 784 | ||
746 | hpet = hpets->hp_hpet; | 785 | hpet = hpetp->hp_hpet; |
747 | t = read_counter(&timer->hpet_compare); | 786 | t = read_counter(&timer->hpet_compare); |
748 | 787 | ||
749 | i = 0; | 788 | i = 0; |
750 | count = hpet_time_div(hpetp->hp_period * TICK_CALIBRATE); | 789 | count = hpet_time_div(hpetp, TICK_CALIBRATE); |
751 | 790 | ||
752 | local_irq_save(flags); | 791 | local_irq_save(flags); |
753 | 792 | ||
@@ -771,28 +810,29 @@ int hpet_alloc(struct hpet_data *hdp) | |||
771 | struct hpets *hpetp; | 810 | struct hpets *hpetp; |
772 | size_t siz; | 811 | size_t siz; |
773 | struct hpet __iomem *hpet; | 812 | struct hpet __iomem *hpet; |
774 | static struct hpets *last = (struct hpets *)0; | 813 | static struct hpets *last = NULL; |
775 | unsigned long ns; | 814 | unsigned long period; |
815 | unsigned long long temp; | ||
776 | 816 | ||
777 | /* | 817 | /* |
778 | * hpet_alloc can be called by platform dependent code. | 818 | * hpet_alloc can be called by platform dependent code. |
779 | * if platform dependent code has allocated the hpet | 819 | * If platform dependent code has allocated the hpet that |
780 | * ACPI also reports hpet, then we catch it here. | 820 | * ACPI has also reported, then we catch it here. |
781 | */ | 821 | */ |
782 | for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next) | 822 | if (hpet_is_known(hdp)) { |
783 | if (hpetp->hp_hpet == hdp->hd_address) | 823 | printk(KERN_DEBUG "%s: duplicate HPET ignored\n", |
784 | return 0; | 824 | __FUNCTION__); |
825 | return 0; | ||
826 | } | ||
785 | 827 | ||
786 | siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) * | 828 | siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) * |
787 | sizeof(struct hpet_dev)); | 829 | sizeof(struct hpet_dev)); |
788 | 830 | ||
789 | hpetp = kmalloc(siz, GFP_KERNEL); | 831 | hpetp = kzalloc(siz, GFP_KERNEL); |
790 | 832 | ||
791 | if (!hpetp) | 833 | if (!hpetp) |
792 | return -ENOMEM; | 834 | return -ENOMEM; |
793 | 835 | ||
794 | memset(hpetp, 0, siz); | ||
795 | |||
796 | hpetp->hp_which = hpet_nhpet++; | 836 | hpetp->hp_which = hpet_nhpet++; |
797 | hpetp->hp_hpet = hdp->hd_address; | 837 | hpetp->hp_hpet = hdp->hd_address; |
798 | hpetp->hp_hpet_phys = hdp->hd_phys_address; | 838 | hpetp->hp_hpet_phys = hdp->hd_phys_address; |
@@ -822,21 +862,23 @@ int hpet_alloc(struct hpet_data *hdp) | |||
822 | 862 | ||
823 | last = hpetp; | 863 | last = hpetp; |
824 | 864 | ||
825 | hpetp->hp_period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >> | 865 | period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >> |
826 | HPET_COUNTER_CLK_PERIOD_SHIFT; | 866 | HPET_COUNTER_CLK_PERIOD_SHIFT; /* fs, 10^-15 */ |
867 | temp = 1000000000000000uLL; /* 10^15 femtoseconds per second */ | ||
868 | temp += period >> 1; /* round */ | ||
869 | do_div(temp, period); | ||
870 | hpetp->hp_tick_freq = temp; /* ticks per second */ | ||
827 | 871 | ||
828 | printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s", | 872 | printk(KERN_INFO "hpet%d: at MMIO 0x%lx (virtual 0x%p), IRQ%s", |
829 | hpetp->hp_which, hdp->hd_phys_address, | 873 | hpetp->hp_which, hdp->hd_phys_address, hdp->hd_address, |
830 | hpetp->hp_ntimer > 1 ? "s" : ""); | 874 | hpetp->hp_ntimer > 1 ? "s" : ""); |
831 | for (i = 0; i < hpetp->hp_ntimer; i++) | 875 | for (i = 0; i < hpetp->hp_ntimer; i++) |
832 | printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); | 876 | printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); |
833 | printk("\n"); | 877 | printk("\n"); |
834 | 878 | ||
835 | ns = hpetp->hp_period; /* femptoseconds, 10^-15 */ | 879 | printk(KERN_INFO "hpet%u: %u %d-bit timers, %Lu Hz\n", |
836 | ns /= 1000000; /* convert to nanoseconds, 10^-9 */ | 880 | hpetp->hp_which, hpetp->hp_ntimer, |
837 | printk(KERN_INFO "hpet%d: %ldns tick, %d %d-bit timers\n", | 881 | cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, hpetp->hp_tick_freq); |
838 | hpetp->hp_which, ns, hpetp->hp_ntimer, | ||
839 | cap & HPET_COUNTER_SIZE_MASK ? 64 : 32); | ||
840 | 882 | ||
841 | mcfg = readq(&hpet->hpet_config); | 883 | mcfg = readq(&hpet->hpet_config); |
842 | if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) { | 884 | if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) { |
@@ -845,13 +887,10 @@ int hpet_alloc(struct hpet_data *hdp) | |||
845 | writeq(mcfg, &hpet->hpet_config); | 887 | writeq(mcfg, &hpet->hpet_config); |
846 | } | 888 | } |
847 | 889 | ||
848 | for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; | 890 | for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; i++, devp++) { |
849 | i++, hpet_ntimer++, devp++) { | ||
850 | unsigned long v; | ||
851 | struct hpet_timer __iomem *timer; | 891 | struct hpet_timer __iomem *timer; |
852 | 892 | ||
853 | timer = &hpet->hpet_timers[devp - hpetp->hp_dev]; | 893 | timer = &hpet->hpet_timers[devp - hpetp->hp_dev]; |
854 | v = readq(&timer->hpet_config); | ||
855 | 894 | ||
856 | devp->hd_hpets = hpetp; | 895 | devp->hd_hpets = hpetp; |
857 | devp->hd_hpet = hpet; | 896 | devp->hd_hpet = hpet; |
@@ -880,7 +919,6 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) | |||
880 | struct hpet_data *hdp; | 919 | struct hpet_data *hdp; |
881 | acpi_status status; | 920 | acpi_status status; |
882 | struct acpi_resource_address64 addr; | 921 | struct acpi_resource_address64 addr; |
883 | struct hpets *hpetp; | ||
884 | 922 | ||
885 | hdp = data; | 923 | hdp = data; |
886 | 924 | ||
@@ -893,9 +931,29 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) | |||
893 | hdp->hd_phys_address = addr.min_address_range; | 931 | hdp->hd_phys_address = addr.min_address_range; |
894 | hdp->hd_address = ioremap(addr.min_address_range, size); | 932 | hdp->hd_address = ioremap(addr.min_address_range, size); |
895 | 933 | ||
896 | for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next) | 934 | if (hpet_is_known(hdp)) { |
897 | if (hpetp->hp_hpet == hdp->hd_address) | 935 | printk(KERN_DEBUG "%s: 0x%lx is busy\n", |
898 | return -EBUSY; | 936 | __FUNCTION__, hdp->hd_phys_address); |
937 | iounmap(hdp->hd_address); | ||
938 | return -EBUSY; | ||
939 | } | ||
940 | } else if (res->id == ACPI_RSTYPE_FIXED_MEM32) { | ||
941 | struct acpi_resource_fixed_mem32 *fixmem32; | ||
942 | |||
943 | fixmem32 = &res->data.fixed_memory32; | ||
944 | if (!fixmem32) | ||
945 | return -EINVAL; | ||
946 | |||
947 | hdp->hd_phys_address = fixmem32->range_base_address; | ||
948 | hdp->hd_address = ioremap(fixmem32->range_base_address, | ||
949 | HPET_RANGE_SIZE); | ||
950 | |||
951 | if (hpet_is_known(hdp)) { | ||
952 | printk(KERN_DEBUG "%s: 0x%lx is busy\n", | ||
953 | __FUNCTION__, hdp->hd_phys_address); | ||
954 | iounmap(hdp->hd_address); | ||
955 | return -EBUSY; | ||
956 | } | ||
899 | } else if (res->id == ACPI_RSTYPE_EXT_IRQ) { | 957 | } else if (res->id == ACPI_RSTYPE_EXT_IRQ) { |
900 | struct acpi_resource_ext_irq *irqp; | 958 | struct acpi_resource_ext_irq *irqp; |
901 | int i; | 959 | int i; |