diff options
-rw-r--r-- | arch/arm/Kconfig | 3 | ||||
-rw-r--r-- | arch/arm/mach-gemini/time.c | 97 |
2 files changed, 91 insertions, 9 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 25ad39a52ecd..e98261cb05bd 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -384,8 +384,9 @@ config ARCH_CLPS711X | |||
384 | config ARCH_GEMINI | 384 | config ARCH_GEMINI |
385 | bool "Cortina Systems Gemini" | 385 | bool "Cortina Systems Gemini" |
386 | select ARCH_REQUIRE_GPIOLIB | 386 | select ARCH_REQUIRE_GPIOLIB |
387 | select ARCH_USES_GETTIMEOFFSET | 387 | select CLKSRC_MMIO |
388 | select CPU_FA526 | 388 | select CPU_FA526 |
389 | select GENERIC_CLOCKEVENTS | ||
389 | select NEED_MACH_GPIO_H | 390 | select NEED_MACH_GPIO_H |
390 | help | 391 | help |
391 | Support for the Cortina Systems Gemini family SoCs | 392 | Support for the Cortina Systems Gemini family SoCs |
diff --git a/arch/arm/mach-gemini/time.c b/arch/arm/mach-gemini/time.c index 21dc5a89d1c4..0a63c4d25b64 100644 --- a/arch/arm/mach-gemini/time.c +++ b/arch/arm/mach-gemini/time.c | |||
@@ -13,6 +13,8 @@ | |||
13 | #include <mach/hardware.h> | 13 | #include <mach/hardware.h> |
14 | #include <mach/global_reg.h> | 14 | #include <mach/global_reg.h> |
15 | #include <asm/mach/time.h> | 15 | #include <asm/mach/time.h> |
16 | #include <linux/clockchips.h> | ||
17 | #include <linux/clocksource.h> | ||
16 | 18 | ||
17 | /* | 19 | /* |
18 | * Register definitions for the timers | 20 | * Register definitions for the timers |
@@ -33,19 +35,89 @@ | |||
33 | #define TIMER_3_CR_CLOCK (1 << 7) | 35 | #define TIMER_3_CR_CLOCK (1 << 7) |
34 | #define TIMER_3_CR_INT (1 << 8) | 36 | #define TIMER_3_CR_INT (1 << 8) |
35 | 37 | ||
38 | static unsigned int tick_rate; | ||
39 | |||
40 | static int gemini_timer_set_next_event(unsigned long cycles, | ||
41 | struct clock_event_device *evt) | ||
42 | { | ||
43 | u32 cr; | ||
44 | |||
45 | cr = readl(TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | ||
46 | |||
47 | /* This may be overdoing it, feel free to test without this */ | ||
48 | cr &= ~TIMER_2_CR_ENABLE; | ||
49 | cr &= ~TIMER_2_CR_INT; | ||
50 | writel(cr, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | ||
51 | |||
52 | /* Set next event */ | ||
53 | writel(cycles, TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER2_BASE))); | ||
54 | writel(cycles, TIMER_LOAD(IO_ADDRESS(GEMINI_TIMER2_BASE))); | ||
55 | cr |= TIMER_2_CR_ENABLE; | ||
56 | cr |= TIMER_2_CR_INT; | ||
57 | writel(cr, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static void gemini_timer_set_mode(enum clock_event_mode mode, | ||
63 | struct clock_event_device *evt) | ||
64 | { | ||
65 | u32 period = DIV_ROUND_CLOSEST(tick_rate, HZ); | ||
66 | u32 cr; | ||
67 | |||
68 | switch (mode) { | ||
69 | case CLOCK_EVT_MODE_PERIODIC: | ||
70 | /* Start the timer */ | ||
71 | writel(period, | ||
72 | TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER2_BASE))); | ||
73 | writel(period, | ||
74 | TIMER_LOAD(IO_ADDRESS(GEMINI_TIMER2_BASE))); | ||
75 | cr = readl(TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | ||
76 | cr |= TIMER_2_CR_ENABLE; | ||
77 | cr |= TIMER_2_CR_INT; | ||
78 | writel(cr, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | ||
79 | break; | ||
80 | case CLOCK_EVT_MODE_ONESHOT: | ||
81 | case CLOCK_EVT_MODE_UNUSED: | ||
82 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
83 | case CLOCK_EVT_MODE_RESUME: | ||
84 | /* | ||
85 | * Disable also for oneshot: the set_next() call will | ||
86 | * arm the timer instead. | ||
87 | */ | ||
88 | cr = readl(TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | ||
89 | cr &= ~TIMER_2_CR_ENABLE; | ||
90 | cr &= ~TIMER_2_CR_INT; | ||
91 | writel(cr, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | ||
92 | break; | ||
93 | default: | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /* Use TIMER2 as clock event */ | ||
99 | static struct clock_event_device gemini_clockevent = { | ||
100 | .name = "TIMER2", | ||
101 | .rating = 300, /* Reasonably fast and accurate clock event */ | ||
102 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
103 | .set_next_event = gemini_timer_set_next_event, | ||
104 | .set_mode = gemini_timer_set_mode, | ||
105 | }; | ||
106 | |||
36 | /* | 107 | /* |
37 | * IRQ handler for the timer | 108 | * IRQ handler for the timer |
38 | */ | 109 | */ |
39 | static irqreturn_t gemini_timer_interrupt(int irq, void *dev_id) | 110 | static irqreturn_t gemini_timer_interrupt(int irq, void *dev_id) |
40 | { | 111 | { |
41 | timer_tick(); | 112 | struct clock_event_device *evt = &gemini_clockevent; |
42 | 113 | ||
114 | evt->event_handler(evt); | ||
43 | return IRQ_HANDLED; | 115 | return IRQ_HANDLED; |
44 | } | 116 | } |
45 | 117 | ||
46 | static struct irqaction gemini_timer_irq = { | 118 | static struct irqaction gemini_timer_irq = { |
47 | .name = "Gemini Timer Tick", | 119 | .name = "Gemini Timer Tick", |
48 | .flags = IRQF_DISABLED | IRQF_TIMER, | 120 | .flags = IRQF_TIMER, |
49 | .handler = gemini_timer_interrupt, | 121 | .handler = gemini_timer_interrupt, |
50 | }; | 122 | }; |
51 | 123 | ||
@@ -54,9 +126,9 @@ static struct irqaction gemini_timer_irq = { | |||
54 | */ | 126 | */ |
55 | void __init gemini_timer_init(void) | 127 | void __init gemini_timer_init(void) |
56 | { | 128 | { |
57 | unsigned int tick_rate, reg_v; | 129 | u32 reg_v; |
58 | 130 | ||
59 | reg_v = __raw_readl(IO_ADDRESS(GEMINI_GLOBAL_BASE + GLOBAL_STATUS)); | 131 | reg_v = readl(IO_ADDRESS(GEMINI_GLOBAL_BASE + GLOBAL_STATUS)); |
60 | tick_rate = REG_TO_AHB_SPEED(reg_v) * 1000000; | 132 | tick_rate = REG_TO_AHB_SPEED(reg_v) * 1000000; |
61 | 133 | ||
62 | printk(KERN_INFO "Bus: %dMHz", tick_rate / 1000000); | 134 | printk(KERN_INFO "Bus: %dMHz", tick_rate / 1000000); |
@@ -82,8 +154,17 @@ void __init gemini_timer_init(void) | |||
82 | * Make irqs happen for the system timer | 154 | * Make irqs happen for the system timer |
83 | */ | 155 | */ |
84 | setup_irq(IRQ_TIMER2, &gemini_timer_irq); | 156 | setup_irq(IRQ_TIMER2, &gemini_timer_irq); |
85 | /* Start the timer */ | 157 | |
86 | __raw_writel(tick_rate / HZ, TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER2_BASE))); | 158 | /* Enable and use TIMER1 as clock source */ |
87 | __raw_writel(tick_rate / HZ, TIMER_LOAD(IO_ADDRESS(GEMINI_TIMER2_BASE))); | 159 | writel(0xffffffff, TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER1_BASE))); |
88 | __raw_writel(TIMER_2_CR_ENABLE | TIMER_2_CR_INT, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | 160 | writel(0xffffffff, TIMER_LOAD(IO_ADDRESS(GEMINI_TIMER1_BASE))); |
161 | writel(TIMER_1_CR_ENABLE, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); | ||
162 | if (clocksource_mmio_init(TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER1_BASE)), | ||
163 | "TIMER1", tick_rate, 300, 32, | ||
164 | clocksource_mmio_readl_up)) | ||
165 | pr_err("timer: failed to initialize gemini clock source\n"); | ||
166 | |||
167 | /* Configure and register the clockevent */ | ||
168 | clockevents_config_and_register(&gemini_clockevent, tick_rate, | ||
169 | 1, 0xffffffff); | ||
89 | } | 170 | } |