diff options
| -rw-r--r-- | arch/arm/Kconfig | 1 | ||||
| -rw-r--r-- | arch/arm/include/asm/hardware/iop3xx.h | 12 | ||||
| -rw-r--r-- | arch/arm/mach-iop13xx/include/mach/time.h | 12 | ||||
| -rw-r--r-- | arch/arm/plat-iop/time.c | 101 |
4 files changed, 115 insertions, 11 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 1c4119c60040..e732fcc30526 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
| @@ -810,6 +810,7 @@ config ARCH_ACORN | |||
| 810 | 810 | ||
| 811 | config PLAT_IOP | 811 | config PLAT_IOP |
| 812 | bool | 812 | bool |
| 813 | select GENERIC_CLOCKEVENTS | ||
| 813 | 814 | ||
| 814 | config PLAT_ORION | 815 | config PLAT_ORION |
| 815 | bool | 816 | bool |
diff --git a/arch/arm/include/asm/hardware/iop3xx.h b/arch/arm/include/asm/hardware/iop3xx.h index 643b7b95b25b..34601b952045 100644 --- a/arch/arm/include/asm/hardware/iop3xx.h +++ b/arch/arm/include/asm/hardware/iop3xx.h | |||
| @@ -236,6 +236,13 @@ void iop_init_cp6_handler(void); | |||
| 236 | void iop_init_time(unsigned long tickrate); | 236 | void iop_init_time(unsigned long tickrate); |
| 237 | unsigned long iop_gettimeoffset(void); | 237 | unsigned long iop_gettimeoffset(void); |
| 238 | 238 | ||
| 239 | static inline u32 read_tmr0(void) | ||
| 240 | { | ||
| 241 | u32 val; | ||
| 242 | asm volatile("mrc p6, 0, %0, c0, c1, 0" : "=r" (val)); | ||
| 243 | return val; | ||
| 244 | } | ||
| 245 | |||
| 239 | static inline void write_tmr0(u32 val) | 246 | static inline void write_tmr0(u32 val) |
| 240 | { | 247 | { |
| 241 | asm volatile("mcr p6, 0, %0, c0, c1, 0" : : "r" (val)); | 248 | asm volatile("mcr p6, 0, %0, c0, c1, 0" : : "r" (val)); |
| @@ -253,6 +260,11 @@ static inline u32 read_tcr0(void) | |||
| 253 | return val; | 260 | return val; |
| 254 | } | 261 | } |
| 255 | 262 | ||
| 263 | static inline void write_tcr0(u32 val) | ||
| 264 | { | ||
| 265 | asm volatile("mcr p6, 0, %0, c2, c1, 0" : : "r" (val)); | ||
| 266 | } | ||
| 267 | |||
| 256 | static inline u32 read_tcr1(void) | 268 | static inline u32 read_tcr1(void) |
| 257 | { | 269 | { |
| 258 | u32 val; | 270 | u32 val; |
diff --git a/arch/arm/mach-iop13xx/include/mach/time.h b/arch/arm/mach-iop13xx/include/mach/time.h index 9fb2768c84bb..b2fb17b35423 100644 --- a/arch/arm/mach-iop13xx/include/mach/time.h +++ b/arch/arm/mach-iop13xx/include/mach/time.h | |||
| @@ -66,6 +66,13 @@ static inline unsigned long iop13xx_xsi_bus_ratio(void) | |||
| 66 | return 2; | 66 | return 2; |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | static inline u32 read_tmr0(void) | ||
| 70 | { | ||
| 71 | u32 val; | ||
| 72 | asm volatile("mrc p6, 0, %0, c0, c9, 0" : "=r" (val)); | ||
| 73 | return val; | ||
| 74 | } | ||
| 75 | |||
| 69 | static inline void write_tmr0(u32 val) | 76 | static inline void write_tmr0(u32 val) |
| 70 | { | 77 | { |
| 71 | asm volatile("mcr p6, 0, %0, c0, c9, 0" : : "r" (val)); | 78 | asm volatile("mcr p6, 0, %0, c0, c9, 0" : : "r" (val)); |
| @@ -83,6 +90,11 @@ static inline u32 read_tcr0(void) | |||
| 83 | return val; | 90 | return val; |
| 84 | } | 91 | } |
| 85 | 92 | ||
| 93 | static inline void write_tcr0(u32 val) | ||
| 94 | { | ||
| 95 | asm volatile("mcr p6, 0, %0, c2, c9, 0" : : "r" (val)); | ||
| 96 | } | ||
| 97 | |||
| 86 | static inline u32 read_tcr1(void) | 98 | static inline u32 read_tcr1(void) |
| 87 | { | 99 | { |
| 88 | u32 val; | 100 | u32 val; |
diff --git a/arch/arm/plat-iop/time.c b/arch/arm/plat-iop/time.c index 5506c9b45612..a550e96394ab 100644 --- a/arch/arm/plat-iop/time.c +++ b/arch/arm/plat-iop/time.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/timex.h> | 20 | #include <linux/timex.h> |
| 21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
| 22 | #include <linux/clocksource.h> | 22 | #include <linux/clocksource.h> |
| 23 | #include <linux/clockchips.h> | ||
| 23 | #include <mach/hardware.h> | 24 | #include <mach/hardware.h> |
| 24 | #include <asm/irq.h> | 25 | #include <asm/irq.h> |
| 25 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
| @@ -64,7 +65,81 @@ static void __init iop_clocksource_set_hz(struct clocksource *cs, unsigned int h | |||
| 64 | cs->name, cs->shift, cs->mult); | 65 | cs->name, cs->shift, cs->mult); |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 68 | /* | ||
| 69 | * IOP clockevents (interrupting timer 0). | ||
| 70 | */ | ||
| 71 | static int iop_set_next_event(unsigned long delta, | ||
| 72 | struct clock_event_device *unused) | ||
| 73 | { | ||
| 74 | u32 tmr = IOP_TMR_PRIVILEGED | IOP_TMR_RATIO_1_1; | ||
| 75 | |||
| 76 | BUG_ON(delta == 0); | ||
| 77 | write_tmr0(tmr & ~(IOP_TMR_EN | IOP_TMR_RELOAD)); | ||
| 78 | write_tcr0(delta); | ||
| 79 | write_tmr0((tmr & ~IOP_TMR_RELOAD) | IOP_TMR_EN); | ||
| 80 | |||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 67 | static unsigned long ticks_per_jiffy; | 84 | static unsigned long ticks_per_jiffy; |
| 85 | |||
| 86 | static void iop_set_mode(enum clock_event_mode mode, | ||
| 87 | struct clock_event_device *unused) | ||
| 88 | { | ||
| 89 | u32 tmr = read_tmr0(); | ||
| 90 | |||
| 91 | switch (mode) { | ||
| 92 | case CLOCK_EVT_MODE_PERIODIC: | ||
| 93 | write_tmr0(tmr & ~IOP_TMR_EN); | ||
| 94 | write_tcr0(ticks_per_jiffy - 1); | ||
| 95 | tmr |= (IOP_TMR_RELOAD | IOP_TMR_EN); | ||
| 96 | break; | ||
| 97 | case CLOCK_EVT_MODE_ONESHOT: | ||
| 98 | /* ->set_next_event sets period and enables timer */ | ||
| 99 | tmr &= ~(IOP_TMR_RELOAD | IOP_TMR_EN); | ||
| 100 | break; | ||
| 101 | case CLOCK_EVT_MODE_RESUME: | ||
| 102 | tmr |= IOP_TMR_EN; | ||
| 103 | break; | ||
| 104 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
| 105 | case CLOCK_EVT_MODE_UNUSED: | ||
| 106 | default: | ||
| 107 | tmr &= ~IOP_TMR_EN; | ||
| 108 | break; | ||
| 109 | } | ||
| 110 | |||
| 111 | write_tmr0(tmr); | ||
| 112 | } | ||
| 113 | |||
| 114 | static struct clock_event_device iop_clockevent = { | ||
| 115 | .name = "iop_timer0", | ||
| 116 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
| 117 | .rating = 300, | ||
| 118 | .set_next_event = iop_set_next_event, | ||
| 119 | .set_mode = iop_set_mode, | ||
| 120 | }; | ||
| 121 | |||
| 122 | static void __init iop_clockevent_set_hz(struct clock_event_device *ce, unsigned int hz) | ||
| 123 | { | ||
| 124 | u64 temp; | ||
| 125 | u32 shift; | ||
| 126 | |||
| 127 | /* Find shift and mult values for hz. */ | ||
| 128 | shift = 32; | ||
| 129 | do { | ||
| 130 | temp = (u64) hz << shift; | ||
| 131 | do_div(temp, NSEC_PER_SEC); | ||
| 132 | if ((temp >> 32) == 0) | ||
| 133 | break; | ||
| 134 | } while (--shift != 0); | ||
| 135 | |||
| 136 | ce->shift = shift; | ||
| 137 | ce->mult = (u32) temp; | ||
| 138 | |||
| 139 | printk(KERN_INFO "clockevent: %s uses shift %u mult %#lx\n", | ||
| 140 | ce->name, ce->shift, ce->mult); | ||
| 141 | } | ||
| 142 | |||
| 68 | static unsigned long ticks_per_usec; | 143 | static unsigned long ticks_per_usec; |
| 69 | static unsigned long next_jiffy_time; | 144 | static unsigned long next_jiffy_time; |
| 70 | 145 | ||
| @@ -95,14 +170,10 @@ unsigned long iop_gettimeoffset(void) | |||
| 95 | static irqreturn_t | 170 | static irqreturn_t |
| 96 | iop_timer_interrupt(int irq, void *dev_id) | 171 | iop_timer_interrupt(int irq, void *dev_id) |
| 97 | { | 172 | { |
| 98 | write_tisr(1); | 173 | struct clock_event_device *evt = dev_id; |
| 99 | |||
| 100 | while ((signed long)(next_jiffy_time - read_tcr1()) | ||
| 101 | >= ticks_per_jiffy) { | ||
| 102 | timer_tick(); | ||
| 103 | next_jiffy_time -= ticks_per_jiffy; | ||
| 104 | } | ||
| 105 | 174 | ||
| 175 | write_tisr(1); | ||
| 176 | evt->event_handler(evt); | ||
| 106 | return IRQ_HANDLED; | 177 | return IRQ_HANDLED; |
| 107 | } | 178 | } |
| 108 | 179 | ||
| @@ -110,6 +181,7 @@ static struct irqaction iop_timer_irq = { | |||
| 110 | .name = "IOP Timer Tick", | 181 | .name = "IOP Timer Tick", |
| 111 | .handler = iop_timer_interrupt, | 182 | .handler = iop_timer_interrupt, |
| 112 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | 183 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
| 184 | .dev_id = &iop_clockevent, | ||
| 113 | }; | 185 | }; |
| 114 | 186 | ||
| 115 | static unsigned long iop_tick_rate; | 187 | static unsigned long iop_tick_rate; |
| @@ -132,10 +204,19 @@ void __init iop_init_time(unsigned long tick_rate) | |||
| 132 | IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; | 204 | IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; |
| 133 | 205 | ||
| 134 | /* | 206 | /* |
| 135 | * We use timer 0 for our timer interrupt, and timer 1 as | 207 | * Set up interrupting clockevent timer 0. |
| 136 | * monotonic counter for tracking missed jiffies. | ||
| 137 | */ | 208 | */ |
| 209 | write_tmr0(timer_ctl & ~IOP_TMR_EN); | ||
| 210 | setup_irq(IRQ_IOP_TIMER0, &iop_timer_irq); | ||
| 211 | iop_clockevent_set_hz(&iop_clockevent, tick_rate); | ||
| 212 | iop_clockevent.max_delta_ns = | ||
| 213 | clockevent_delta2ns(0xfffffffe, &iop_clockevent); | ||
| 214 | iop_clockevent.min_delta_ns = | ||
| 215 | clockevent_delta2ns(0xf, &iop_clockevent); | ||
| 216 | iop_clockevent.cpumask = cpumask_of(0); | ||
| 217 | clockevents_register_device(&iop_clockevent); | ||
| 138 | write_trr0(ticks_per_jiffy - 1); | 218 | write_trr0(ticks_per_jiffy - 1); |
| 219 | write_tcr0(ticks_per_jiffy - 1); | ||
| 139 | write_tmr0(timer_ctl); | 220 | write_tmr0(timer_ctl); |
| 140 | 221 | ||
| 141 | /* | 222 | /* |
| @@ -146,6 +227,4 @@ void __init iop_init_time(unsigned long tick_rate) | |||
| 146 | write_tmr1(timer_ctl); | 227 | write_tmr1(timer_ctl); |
| 147 | iop_clocksource_set_hz(&iop_clocksource, tick_rate); | 228 | iop_clocksource_set_hz(&iop_clocksource, tick_rate); |
| 148 | clocksource_register(&iop_clocksource); | 229 | clocksource_register(&iop_clocksource); |
| 149 | |||
| 150 | setup_irq(IRQ_IOP_TIMER0, &iop_timer_irq); | ||
| 151 | } | 230 | } |
