diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-01-14 08:30:16 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-05-02 04:35:34 -0400 |
commit | e388771458b4ff3ad81ab70e390b24d069647da4 (patch) | |
tree | 43f130464e60cabf5a1357355277f3d959cd1789 /arch/arm | |
parent | f4b8b319bf21bf3576014ce7336763cd3e1684ef (diff) |
ARM: Realview/Versatile: separate out common SP804 timer code
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/Kconfig | 5 | ||||
-rw-r--r-- | arch/arm/mach-realview/core.c | 139 | ||||
-rw-r--r-- | arch/arm/mach-versatile/core.c | 135 | ||||
-rw-r--r-- | arch/arm/plat-versatile/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-versatile/include/plat/timer-sp.h | 2 | ||||
-rw-r--r-- | arch/arm/plat-versatile/timer-sp.c | 168 |
6 files changed, 182 insertions, 268 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e8265f257674..553487052772 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -257,6 +257,7 @@ config ARCH_REALVIEW | |||
257 | select GENERIC_CLOCKEVENTS | 257 | select GENERIC_CLOCKEVENTS |
258 | select ARCH_WANT_OPTIONAL_GPIOLIB | 258 | select ARCH_WANT_OPTIONAL_GPIOLIB |
259 | select PLAT_VERSATILE | 259 | select PLAT_VERSATILE |
260 | select ARM_TIMER_SP804 | ||
260 | help | 261 | help |
261 | This enables support for ARM Ltd RealView boards. | 262 | This enables support for ARM Ltd RealView boards. |
262 | 263 | ||
@@ -271,6 +272,7 @@ config ARCH_VERSATILE | |||
271 | select GENERIC_CLOCKEVENTS | 272 | select GENERIC_CLOCKEVENTS |
272 | select ARCH_WANT_OPTIONAL_GPIOLIB | 273 | select ARCH_WANT_OPTIONAL_GPIOLIB |
273 | select PLAT_VERSATILE | 274 | select PLAT_VERSATILE |
275 | select ARM_TIMER_SP804 | ||
274 | help | 276 | help |
275 | This enables support for ARM Ltd Versatile board. | 277 | This enables support for ARM Ltd Versatile board. |
276 | 278 | ||
@@ -944,6 +946,9 @@ config PLAT_PXA | |||
944 | config PLAT_VERSATILE | 946 | config PLAT_VERSATILE |
945 | bool | 947 | bool |
946 | 948 | ||
949 | config ARM_TIMER_SP804 | ||
950 | bool | ||
951 | |||
947 | source arch/arm/mm/Kconfig | 952 | source arch/arm/mm/Kconfig |
948 | 953 | ||
949 | config IWMMXT | 954 | config IWMMXT |
diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 17eb7eb780d8..80b8142463c1 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c | |||
@@ -25,8 +25,6 @@ | |||
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <linux/amba/bus.h> | 26 | #include <linux/amba/bus.h> |
27 | #include <linux/amba/clcd.h> | 27 | #include <linux/amba/clcd.h> |
28 | #include <linux/clocksource.h> | ||
29 | #include <linux/clockchips.h> | ||
30 | #include <linux/io.h> | 28 | #include <linux/io.h> |
31 | #include <linux/smsc911x.h> | 29 | #include <linux/smsc911x.h> |
32 | #include <linux/ata_platform.h> | 30 | #include <linux/ata_platform.h> |
@@ -51,6 +49,7 @@ | |||
51 | #include <mach/clkdev.h> | 49 | #include <mach/clkdev.h> |
52 | #include <mach/platform.h> | 50 | #include <mach/platform.h> |
53 | #include <mach/irqs.h> | 51 | #include <mach/irqs.h> |
52 | #include <plat/timer-sp.h> | ||
54 | 53 | ||
55 | #include "core.h" | 54 | #include "core.h" |
56 | 55 | ||
@@ -646,133 +645,6 @@ void __iomem *timer2_va_base; | |||
646 | void __iomem *timer3_va_base; | 645 | void __iomem *timer3_va_base; |
647 | 646 | ||
648 | /* | 647 | /* |
649 | * How long is the timer interval? | ||
650 | */ | ||
651 | #define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10) | ||
652 | #if TIMER_INTERVAL >= 0x100000 | ||
653 | #define TIMER_RELOAD (TIMER_INTERVAL >> 8) | ||
654 | #define TIMER_DIVISOR (TIMER_CTRL_DIV256) | ||
655 | #define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC) | ||
656 | #elif TIMER_INTERVAL >= 0x10000 | ||
657 | #define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */ | ||
658 | #define TIMER_DIVISOR (TIMER_CTRL_DIV16) | ||
659 | #define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC) | ||
660 | #else | ||
661 | #define TIMER_RELOAD (TIMER_INTERVAL) | ||
662 | #define TIMER_DIVISOR (TIMER_CTRL_DIV1) | ||
663 | #define TICKS2USECS(x) ((x) / TICKS_PER_uSEC) | ||
664 | #endif | ||
665 | |||
666 | static void timer_set_mode(enum clock_event_mode mode, | ||
667 | struct clock_event_device *clk) | ||
668 | { | ||
669 | unsigned long ctrl; | ||
670 | |||
671 | switch(mode) { | ||
672 | case CLOCK_EVT_MODE_PERIODIC: | ||
673 | writel(TIMER_RELOAD, timer0_va_base + TIMER_LOAD); | ||
674 | |||
675 | ctrl = TIMER_CTRL_PERIODIC; | ||
676 | ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE | TIMER_CTRL_ENABLE; | ||
677 | break; | ||
678 | case CLOCK_EVT_MODE_ONESHOT: | ||
679 | /* period set, and timer enabled in 'next_event' hook */ | ||
680 | ctrl = TIMER_CTRL_ONESHOT; | ||
681 | ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE; | ||
682 | break; | ||
683 | case CLOCK_EVT_MODE_UNUSED: | ||
684 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
685 | default: | ||
686 | ctrl = 0; | ||
687 | } | ||
688 | |||
689 | writel(ctrl, timer0_va_base + TIMER_CTRL); | ||
690 | } | ||
691 | |||
692 | static int timer_set_next_event(unsigned long evt, | ||
693 | struct clock_event_device *unused) | ||
694 | { | ||
695 | unsigned long ctrl = readl(timer0_va_base + TIMER_CTRL); | ||
696 | |||
697 | writel(evt, timer0_va_base + TIMER_LOAD); | ||
698 | writel(ctrl | TIMER_CTRL_ENABLE, timer0_va_base + TIMER_CTRL); | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static struct clock_event_device timer0_clockevent = { | ||
704 | .name = "timer0", | ||
705 | .shift = 32, | ||
706 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
707 | .set_mode = timer_set_mode, | ||
708 | .set_next_event = timer_set_next_event, | ||
709 | .rating = 300, | ||
710 | .cpumask = cpu_all_mask, | ||
711 | }; | ||
712 | |||
713 | static void __init realview_clockevents_init(unsigned int timer_irq) | ||
714 | { | ||
715 | timer0_clockevent.irq = timer_irq; | ||
716 | timer0_clockevent.mult = | ||
717 | div_sc(1000000, NSEC_PER_SEC, timer0_clockevent.shift); | ||
718 | timer0_clockevent.max_delta_ns = | ||
719 | clockevent_delta2ns(0xffffffff, &timer0_clockevent); | ||
720 | timer0_clockevent.min_delta_ns = | ||
721 | clockevent_delta2ns(0xf, &timer0_clockevent); | ||
722 | |||
723 | clockevents_register_device(&timer0_clockevent); | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * IRQ handler for the timer | ||
728 | */ | ||
729 | static irqreturn_t realview_timer_interrupt(int irq, void *dev_id) | ||
730 | { | ||
731 | struct clock_event_device *evt = &timer0_clockevent; | ||
732 | |||
733 | /* clear the interrupt */ | ||
734 | writel(1, timer0_va_base + TIMER_INTCLR); | ||
735 | |||
736 | evt->event_handler(evt); | ||
737 | |||
738 | return IRQ_HANDLED; | ||
739 | } | ||
740 | |||
741 | static struct irqaction realview_timer_irq = { | ||
742 | .name = "RealView Timer Tick", | ||
743 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
744 | .handler = realview_timer_interrupt, | ||
745 | }; | ||
746 | |||
747 | static cycle_t realview_get_cycles(struct clocksource *cs) | ||
748 | { | ||
749 | return ~readl(timer3_va_base + TIMER_VALUE); | ||
750 | } | ||
751 | |||
752 | static struct clocksource clocksource_realview = { | ||
753 | .name = "timer3", | ||
754 | .rating = 200, | ||
755 | .read = realview_get_cycles, | ||
756 | .mask = CLOCKSOURCE_MASK(32), | ||
757 | .shift = 20, | ||
758 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
759 | }; | ||
760 | |||
761 | static void __init realview_clocksource_init(void) | ||
762 | { | ||
763 | /* setup timer 0 as free-running clocksource */ | ||
764 | writel(0, timer3_va_base + TIMER_CTRL); | ||
765 | writel(0xffffffff, timer3_va_base + TIMER_LOAD); | ||
766 | writel(0xffffffff, timer3_va_base + TIMER_VALUE); | ||
767 | writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, | ||
768 | timer3_va_base + TIMER_CTRL); | ||
769 | |||
770 | clocksource_realview.mult = | ||
771 | clocksource_khz2mult(1000, clocksource_realview.shift); | ||
772 | clocksource_register(&clocksource_realview); | ||
773 | } | ||
774 | |||
775 | /* | ||
776 | * Set up the clock source and clock events devices | 648 | * Set up the clock source and clock events devices |
777 | */ | 649 | */ |
778 | void __init realview_timer_init(unsigned int timer_irq) | 650 | void __init realview_timer_init(unsigned int timer_irq) |
@@ -799,13 +671,8 @@ void __init realview_timer_init(unsigned int timer_irq) | |||
799 | writel(0, timer2_va_base + TIMER_CTRL); | 671 | writel(0, timer2_va_base + TIMER_CTRL); |
800 | writel(0, timer3_va_base + TIMER_CTRL); | 672 | writel(0, timer3_va_base + TIMER_CTRL); |
801 | 673 | ||
802 | /* | 674 | sp804_clocksource_init(timer3_va_base); |
803 | * Make irqs happen for the system timer | 675 | sp804_clockevents_init(timer0_va_base, timer_irq); |
804 | */ | ||
805 | setup_irq(timer_irq, &realview_timer_irq); | ||
806 | |||
807 | realview_clocksource_init(); | ||
808 | realview_clockevents_init(timer_irq); | ||
809 | } | 676 | } |
810 | 677 | ||
811 | /* | 678 | /* |
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index e9d255f33674..b68ddd349f12 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c | |||
@@ -28,8 +28,6 @@ | |||
28 | #include <linux/amba/clcd.h> | 28 | #include <linux/amba/clcd.h> |
29 | #include <linux/amba/pl061.h> | 29 | #include <linux/amba/pl061.h> |
30 | #include <linux/amba/mmci.h> | 30 | #include <linux/amba/mmci.h> |
31 | #include <linux/clocksource.h> | ||
32 | #include <linux/clockchips.h> | ||
33 | #include <linux/cnt32_to_63.h> | 31 | #include <linux/cnt32_to_63.h> |
34 | #include <linux/io.h> | 32 | #include <linux/io.h> |
35 | 33 | ||
@@ -50,6 +48,7 @@ | |||
50 | #include <mach/clkdev.h> | 48 | #include <mach/clkdev.h> |
51 | #include <mach/hardware.h> | 49 | #include <mach/hardware.h> |
52 | #include <mach/platform.h> | 50 | #include <mach/platform.h> |
51 | #include <plat/timer-sp.h> | ||
53 | 52 | ||
54 | #include "core.h" | 53 | #include "core.h" |
55 | 54 | ||
@@ -877,120 +876,6 @@ void __init versatile_init(void) | |||
877 | #define TIMER1_VA_BASE (__io_address(VERSATILE_TIMER0_1_BASE) + 0x20) | 876 | #define TIMER1_VA_BASE (__io_address(VERSATILE_TIMER0_1_BASE) + 0x20) |
878 | #define TIMER2_VA_BASE __io_address(VERSATILE_TIMER2_3_BASE) | 877 | #define TIMER2_VA_BASE __io_address(VERSATILE_TIMER2_3_BASE) |
879 | #define TIMER3_VA_BASE (__io_address(VERSATILE_TIMER2_3_BASE) + 0x20) | 878 | #define TIMER3_VA_BASE (__io_address(VERSATILE_TIMER2_3_BASE) + 0x20) |
880 | #define VA_IC_BASE __io_address(VERSATILE_VIC_BASE) | ||
881 | |||
882 | /* | ||
883 | * How long is the timer interval? | ||
884 | */ | ||
885 | #define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10) | ||
886 | #if TIMER_INTERVAL >= 0x100000 | ||
887 | #define TIMER_RELOAD (TIMER_INTERVAL >> 8) | ||
888 | #define TIMER_DIVISOR (TIMER_CTRL_DIV256) | ||
889 | #define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC) | ||
890 | #elif TIMER_INTERVAL >= 0x10000 | ||
891 | #define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */ | ||
892 | #define TIMER_DIVISOR (TIMER_CTRL_DIV16) | ||
893 | #define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC) | ||
894 | #else | ||
895 | #define TIMER_RELOAD (TIMER_INTERVAL) | ||
896 | #define TIMER_DIVISOR (TIMER_CTRL_DIV1) | ||
897 | #define TICKS2USECS(x) ((x) / TICKS_PER_uSEC) | ||
898 | #endif | ||
899 | |||
900 | static void timer_set_mode(enum clock_event_mode mode, | ||
901 | struct clock_event_device *clk) | ||
902 | { | ||
903 | unsigned long ctrl; | ||
904 | |||
905 | switch(mode) { | ||
906 | case CLOCK_EVT_MODE_PERIODIC: | ||
907 | writel(TIMER_RELOAD, TIMER0_VA_BASE + TIMER_LOAD); | ||
908 | |||
909 | ctrl = TIMER_CTRL_PERIODIC; | ||
910 | ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE | TIMER_CTRL_ENABLE; | ||
911 | break; | ||
912 | case CLOCK_EVT_MODE_ONESHOT: | ||
913 | /* period set, and timer enabled in 'next_event' hook */ | ||
914 | ctrl = TIMER_CTRL_ONESHOT; | ||
915 | ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE; | ||
916 | break; | ||
917 | case CLOCK_EVT_MODE_UNUSED: | ||
918 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
919 | default: | ||
920 | ctrl = 0; | ||
921 | } | ||
922 | |||
923 | writel(ctrl, TIMER0_VA_BASE + TIMER_CTRL); | ||
924 | } | ||
925 | |||
926 | static int timer_set_next_event(unsigned long evt, | ||
927 | struct clock_event_device *unused) | ||
928 | { | ||
929 | unsigned long ctrl = readl(TIMER0_VA_BASE + TIMER_CTRL); | ||
930 | |||
931 | writel(evt, TIMER0_VA_BASE + TIMER_LOAD); | ||
932 | writel(ctrl | TIMER_CTRL_ENABLE, TIMER0_VA_BASE + TIMER_CTRL); | ||
933 | |||
934 | return 0; | ||
935 | } | ||
936 | |||
937 | static struct clock_event_device timer0_clockevent = { | ||
938 | .name = "timer0", | ||
939 | .shift = 32, | ||
940 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
941 | .set_mode = timer_set_mode, | ||
942 | .set_next_event = timer_set_next_event, | ||
943 | }; | ||
944 | |||
945 | /* | ||
946 | * IRQ handler for the timer | ||
947 | */ | ||
948 | static irqreturn_t versatile_timer_interrupt(int irq, void *dev_id) | ||
949 | { | ||
950 | struct clock_event_device *evt = &timer0_clockevent; | ||
951 | |||
952 | writel(1, TIMER0_VA_BASE + TIMER_INTCLR); | ||
953 | |||
954 | evt->event_handler(evt); | ||
955 | |||
956 | return IRQ_HANDLED; | ||
957 | } | ||
958 | |||
959 | static struct irqaction versatile_timer_irq = { | ||
960 | .name = "Versatile Timer Tick", | ||
961 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
962 | .handler = versatile_timer_interrupt, | ||
963 | }; | ||
964 | |||
965 | static cycle_t versatile_get_cycles(struct clocksource *cs) | ||
966 | { | ||
967 | return ~readl(TIMER3_VA_BASE + TIMER_VALUE); | ||
968 | } | ||
969 | |||
970 | static struct clocksource clocksource_versatile = { | ||
971 | .name = "timer3", | ||
972 | .rating = 200, | ||
973 | .read = versatile_get_cycles, | ||
974 | .mask = CLOCKSOURCE_MASK(32), | ||
975 | .shift = 20, | ||
976 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
977 | }; | ||
978 | |||
979 | static int __init versatile_clocksource_init(void) | ||
980 | { | ||
981 | /* setup timer3 as free-running clocksource */ | ||
982 | writel(0, TIMER3_VA_BASE + TIMER_CTRL); | ||
983 | writel(0xffffffff, TIMER3_VA_BASE + TIMER_LOAD); | ||
984 | writel(0xffffffff, TIMER3_VA_BASE + TIMER_VALUE); | ||
985 | writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, | ||
986 | TIMER3_VA_BASE + TIMER_CTRL); | ||
987 | |||
988 | clocksource_versatile.mult = | ||
989 | clocksource_khz2mult(1000, clocksource_versatile.shift); | ||
990 | clocksource_register(&clocksource_versatile); | ||
991 | |||
992 | return 0; | ||
993 | } | ||
994 | 879 | ||
995 | /* | 880 | /* |
996 | * Set up timer interrupt, and return the current time in seconds. | 881 | * Set up timer interrupt, and return the current time in seconds. |
@@ -1019,22 +904,8 @@ static void __init versatile_timer_init(void) | |||
1019 | writel(0, TIMER2_VA_BASE + TIMER_CTRL); | 904 | writel(0, TIMER2_VA_BASE + TIMER_CTRL); |
1020 | writel(0, TIMER3_VA_BASE + TIMER_CTRL); | 905 | writel(0, TIMER3_VA_BASE + TIMER_CTRL); |
1021 | 906 | ||
1022 | /* | 907 | sp804_clocksource_init(TIMER3_VA_BASE); |
1023 | * Make irqs happen for the system timer | 908 | sp804_clockevents_init(TIMER0_VA_BASE, IRQ_TIMERINT0_1); |
1024 | */ | ||
1025 | setup_irq(IRQ_TIMERINT0_1, &versatile_timer_irq); | ||
1026 | |||
1027 | versatile_clocksource_init(); | ||
1028 | |||
1029 | timer0_clockevent.mult = | ||
1030 | div_sc(1000000, NSEC_PER_SEC, timer0_clockevent.shift); | ||
1031 | timer0_clockevent.max_delta_ns = | ||
1032 | clockevent_delta2ns(0xffffffff, &timer0_clockevent); | ||
1033 | timer0_clockevent.min_delta_ns = | ||
1034 | clockevent_delta2ns(0xf, &timer0_clockevent); | ||
1035 | |||
1036 | timer0_clockevent.cpumask = cpumask_of(0); | ||
1037 | clockevents_register_device(&timer0_clockevent); | ||
1038 | } | 909 | } |
1039 | 910 | ||
1040 | struct sys_timer versatile_timer = { | 911 | struct sys_timer versatile_timer = { |
diff --git a/arch/arm/plat-versatile/Makefile b/arch/arm/plat-versatile/Makefile index 2228fd1725ac..334d2f14232c 100644 --- a/arch/arm/plat-versatile/Makefile +++ b/arch/arm/plat-versatile/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-y := clock.o | 1 | obj-y := clock.o |
2 | obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o | ||
diff --git a/arch/arm/plat-versatile/include/plat/timer-sp.h b/arch/arm/plat-versatile/include/plat/timer-sp.h new file mode 100644 index 000000000000..21e75e30d497 --- /dev/null +++ b/arch/arm/plat-versatile/include/plat/timer-sp.h | |||
@@ -0,0 +1,2 @@ | |||
1 | void sp804_clocksource_init(void __iomem *); | ||
2 | void sp804_clockevents_init(void __iomem *, unsigned int); | ||
diff --git a/arch/arm/plat-versatile/timer-sp.c b/arch/arm/plat-versatile/timer-sp.c new file mode 100644 index 000000000000..98722f44640c --- /dev/null +++ b/arch/arm/plat-versatile/timer-sp.c | |||
@@ -0,0 +1,168 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-versatile/timer-sp.c | ||
3 | * | ||
4 | * Copyright (C) 1999 - 2003 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | #include <linux/clocksource.h> | ||
22 | #include <linux/clockchips.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/irq.h> | ||
25 | #include <linux/io.h> | ||
26 | |||
27 | #include <asm/hardware/arm_timer.h> | ||
28 | |||
29 | #include <mach/platform.h> | ||
30 | |||
31 | #include <plat/timer-sp.h> | ||
32 | |||
33 | /* | ||
34 | * How long is the timer interval? | ||
35 | */ | ||
36 | #define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10) | ||
37 | #if TIMER_INTERVAL >= 0x100000 | ||
38 | #define TIMER_RELOAD (TIMER_INTERVAL >> 8) | ||
39 | #define TIMER_DIVISOR (TIMER_CTRL_DIV256) | ||
40 | #elif TIMER_INTERVAL >= 0x10000 | ||
41 | #define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */ | ||
42 | #define TIMER_DIVISOR (TIMER_CTRL_DIV16) | ||
43 | #else | ||
44 | #define TIMER_RELOAD (TIMER_INTERVAL) | ||
45 | #define TIMER_DIVISOR (TIMER_CTRL_DIV1) | ||
46 | #endif | ||
47 | |||
48 | |||
49 | static void __iomem *clksrc_base; | ||
50 | |||
51 | static cycle_t sp804_read(struct clocksource *cs) | ||
52 | { | ||
53 | return ~readl(clksrc_base + TIMER_VALUE); | ||
54 | } | ||
55 | |||
56 | static struct clocksource clocksource_sp804 = { | ||
57 | .name = "timer3", | ||
58 | .rating = 200, | ||
59 | .read = sp804_read, | ||
60 | .mask = CLOCKSOURCE_MASK(32), | ||
61 | .shift = 20, | ||
62 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
63 | }; | ||
64 | |||
65 | void __init sp804_clocksource_init(void __iomem *base) | ||
66 | { | ||
67 | struct clocksource *cs = &clocksource_sp804; | ||
68 | |||
69 | clksrc_base = base; | ||
70 | |||
71 | /* setup timer 0 as free-running clocksource */ | ||
72 | writel(0, clksrc_base + TIMER_CTRL); | ||
73 | writel(0xffffffff, clksrc_base + TIMER_LOAD); | ||
74 | writel(0xffffffff, clksrc_base + TIMER_VALUE); | ||
75 | writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, | ||
76 | clksrc_base + TIMER_CTRL); | ||
77 | |||
78 | cs->mult = clocksource_khz2mult(1000, cs->shift); | ||
79 | clocksource_register(cs); | ||
80 | } | ||
81 | |||
82 | |||
83 | static void __iomem *clkevt_base; | ||
84 | |||
85 | /* | ||
86 | * IRQ handler for the timer | ||
87 | */ | ||
88 | static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id) | ||
89 | { | ||
90 | struct clock_event_device *evt = dev_id; | ||
91 | |||
92 | /* clear the interrupt */ | ||
93 | writel(1, clkevt_base + TIMER_INTCLR); | ||
94 | |||
95 | evt->event_handler(evt); | ||
96 | |||
97 | return IRQ_HANDLED; | ||
98 | } | ||
99 | |||
100 | static void sp804_set_mode(enum clock_event_mode mode, | ||
101 | struct clock_event_device *evt) | ||
102 | { | ||
103 | unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE; | ||
104 | |||
105 | writel(ctrl, clkevt_base + TIMER_CTRL); | ||
106 | |||
107 | switch (mode) { | ||
108 | case CLOCK_EVT_MODE_PERIODIC: | ||
109 | writel(TIMER_RELOAD, clkevt_base + TIMER_LOAD); | ||
110 | ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; | ||
111 | break; | ||
112 | |||
113 | case CLOCK_EVT_MODE_ONESHOT: | ||
114 | /* period set, and timer enabled in 'next_event' hook */ | ||
115 | ctrl |= TIMER_CTRL_ONESHOT; | ||
116 | break; | ||
117 | |||
118 | case CLOCK_EVT_MODE_UNUSED: | ||
119 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
120 | default: | ||
121 | break; | ||
122 | } | ||
123 | |||
124 | writel(ctrl, clkevt_base + TIMER_CTRL); | ||
125 | } | ||
126 | |||
127 | static int sp804_set_next_event(unsigned long next, | ||
128 | struct clock_event_device *evt) | ||
129 | { | ||
130 | unsigned long ctrl = readl(clkevt_base + TIMER_CTRL); | ||
131 | |||
132 | writel(next, clkevt_base + TIMER_LOAD); | ||
133 | writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static struct clock_event_device sp804_clockevent = { | ||
139 | .name = "timer0", | ||
140 | .shift = 32, | ||
141 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
142 | .set_mode = sp804_set_mode, | ||
143 | .set_next_event = sp804_set_next_event, | ||
144 | .rating = 300, | ||
145 | .cpumask = cpu_all_mask, | ||
146 | }; | ||
147 | |||
148 | static struct irqaction sp804_timer_irq = { | ||
149 | .name = "timer", | ||
150 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
151 | .handler = sp804_timer_interrupt, | ||
152 | .dev_id = &sp804_clockevent, | ||
153 | }; | ||
154 | |||
155 | void __init sp804_clockevents_init(void __iomem *base, unsigned int timer_irq) | ||
156 | { | ||
157 | struct clock_event_device *evt = &sp804_clockevent; | ||
158 | |||
159 | clkevt_base = base; | ||
160 | |||
161 | evt->irq = timer_irq; | ||
162 | evt->mult = div_sc(1000000, NSEC_PER_SEC, evt->shift); | ||
163 | evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); | ||
164 | evt->min_delta_ns = clockevent_delta2ns(0xf, evt); | ||
165 | |||
166 | setup_irq(timer_irq, &sp804_timer_irq); | ||
167 | clockevents_register_device(evt); | ||
168 | } | ||