| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * arch/arm/plat-iop/time.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Timer code for IOP32x and IOP33x based systems | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Author: Deepak Saxena <dsaxena@mvista.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright 2002-2003 MontaVista Software Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms of the GNU General Public License as published by the | 
					
						
							|  |  |  |  * Free Software Foundation; either version 2 of the License, or (at your | 
					
						
							|  |  |  |  * option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/time.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/timex.h>
 | 
					
						
							| 
									
										
										
										
											2008-09-06 12:10:45 +01:00
										 |  |  | #include <linux/io.h>
 | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | #include <linux/clocksource.h>
 | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | #include <linux/clockchips.h>
 | 
					
						
							| 
									
										
										
										
											2011-07-31 16:17:29 -04:00
										 |  |  | #include <linux/export.h>
 | 
					
						
							| 
									
										
										
										
											2008-08-05 16:14:15 +01:00
										 |  |  | #include <mach/hardware.h>
 | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | #include <asm/irq.h>
 | 
					
						
							| 
									
										
										
										
											2010-12-15 21:52:10 +00:00
										 |  |  | #include <asm/sched_clock.h>
 | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | #include <asm/uaccess.h>
 | 
					
						
							|  |  |  | #include <asm/mach/irq.h>
 | 
					
						
							|  |  |  | #include <asm/mach/time.h>
 | 
					
						
							| 
									
										
										
										
											2008-08-05 16:14:15 +01:00
										 |  |  | #include <mach/time.h>
 | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-02 09:08:55 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Minimum clocksource/clockevent timer range in seconds | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define IOP_MIN_RANGE 4
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * IOP clocksource (free-running timer 1). | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-12-04 06:20:52 +01:00
										 |  |  | static cycle_t notrace iop_clocksource_read(struct clocksource *unused) | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	return 0xffffffffu - read_tcr1(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct clocksource iop_clocksource = { | 
					
						
							|  |  |  | 	.name 		= "iop_timer1", | 
					
						
							|  |  |  | 	.rating		= 300, | 
					
						
							|  |  |  | 	.read		= iop_clocksource_read, | 
					
						
							|  |  |  | 	.mask		= CLOCKSOURCE_MASK(32), | 
					
						
							|  |  |  | 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:56 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * IOP sched_clock() implementation via its clocksource. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-12-15 12:19:23 +01:00
										 |  |  | static u32 notrace iop_read_sched_clock(void) | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:56 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-15 12:19:23 +01:00
										 |  |  | 	return 0xffffffffu - read_tcr1(); | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * IOP clockevents (interrupting timer 0). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int iop_set_next_event(unsigned long delta, | 
					
						
							|  |  |  | 			      struct clock_event_device *unused) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 tmr = IOP_TMR_PRIVILEGED | IOP_TMR_RATIO_1_1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	BUG_ON(delta == 0); | 
					
						
							|  |  |  | 	write_tmr0(tmr & ~(IOP_TMR_EN | IOP_TMR_RELOAD)); | 
					
						
							|  |  |  | 	write_tcr0(delta); | 
					
						
							|  |  |  | 	write_tmr0((tmr & ~IOP_TMR_RELOAD) | IOP_TMR_EN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | static unsigned long ticks_per_jiffy; | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void iop_set_mode(enum clock_event_mode mode, | 
					
						
							|  |  |  | 			 struct clock_event_device *unused) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 tmr = read_tmr0(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (mode) { | 
					
						
							|  |  |  | 	case CLOCK_EVT_MODE_PERIODIC: | 
					
						
							|  |  |  | 		write_tmr0(tmr & ~IOP_TMR_EN); | 
					
						
							|  |  |  | 		write_tcr0(ticks_per_jiffy - 1); | 
					
						
							| 
									
										
										
										
											2010-12-19 15:43:34 +00:00
										 |  |  | 		write_trr0(ticks_per_jiffy - 1); | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 		tmr |= (IOP_TMR_RELOAD | IOP_TMR_EN); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case CLOCK_EVT_MODE_ONESHOT: | 
					
						
							|  |  |  | 		/* ->set_next_event sets period and enables timer */ | 
					
						
							|  |  |  | 		tmr &= ~(IOP_TMR_RELOAD | IOP_TMR_EN); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case CLOCK_EVT_MODE_RESUME: | 
					
						
							|  |  |  | 		tmr |= IOP_TMR_EN; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case CLOCK_EVT_MODE_SHUTDOWN: | 
					
						
							|  |  |  | 	case CLOCK_EVT_MODE_UNUSED: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		tmr &= ~IOP_TMR_EN; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	write_tmr0(tmr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct clock_event_device iop_clockevent = { | 
					
						
							|  |  |  | 	.name		= "iop_timer0", | 
					
						
							|  |  |  | 	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 
					
						
							|  |  |  | 	.rating         = 300, | 
					
						
							|  |  |  | 	.set_next_event	= iop_set_next_event, | 
					
						
							|  |  |  | 	.set_mode	= iop_set_mode, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | static irqreturn_t | 
					
						
							| 
									
										
										
										
											2007-02-13 17:13:34 +01:00
										 |  |  | iop_timer_interrupt(int irq, void *dev_id) | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 	struct clock_event_device *evt = dev_id; | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 	write_tisr(1); | 
					
						
							|  |  |  | 	evt->event_handler(evt); | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | 	return IRQ_HANDLED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-13 17:13:34 +01:00
										 |  |  | static struct irqaction iop_timer_irq = { | 
					
						
							|  |  |  | 	.name		= "IOP Timer Tick", | 
					
						
							|  |  |  | 	.handler	= iop_timer_interrupt, | 
					
						
							| 
									
										
										
										
											2007-05-08 00:35:39 -07:00
										 |  |  | 	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 	.dev_id		= &iop_clockevent, | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-20 02:07:26 +01:00
										 |  |  | static unsigned long iop_tick_rate; | 
					
						
							|  |  |  | unsigned long get_iop_tick_rate(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return iop_tick_rate; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(get_iop_tick_rate); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-13 17:13:34 +01:00
										 |  |  | void __init iop_init_time(unsigned long tick_rate) | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	u32 timer_ctl; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-15 12:19:23 +01:00
										 |  |  | 	setup_sched_clock(iop_read_sched_clock, 32, tick_rate); | 
					
						
							| 
									
										
										
										
											2010-12-15 21:52:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-02 10:46:45 +02:00
										 |  |  | 	ticks_per_jiffy = DIV_ROUND_CLOSEST(tick_rate, HZ); | 
					
						
							| 
									
										
										
										
											2007-07-20 02:07:26 +01:00
										 |  |  | 	iop_tick_rate = tick_rate; | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-13 17:13:34 +01:00
										 |  |  | 	timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED | | 
					
						
							|  |  |  | 			IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 	 * Set up interrupting clockevent timer 0. | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 	write_tmr0(timer_ctl & ~IOP_TMR_EN); | 
					
						
							| 
									
										
										
										
											2010-12-19 15:43:34 +00:00
										 |  |  | 	write_tisr(1); | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 	setup_irq(IRQ_IOP_TIMER0, &iop_timer_irq); | 
					
						
							| 
									
										
										
										
											2010-06-02 09:08:55 +01:00
										 |  |  | 	clockevents_calc_mult_shift(&iop_clockevent, | 
					
						
							|  |  |  | 				    tick_rate, IOP_MIN_RANGE); | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 	iop_clockevent.max_delta_ns = | 
					
						
							|  |  |  | 		clockevent_delta2ns(0xfffffffe, &iop_clockevent); | 
					
						
							|  |  |  | 	iop_clockevent.min_delta_ns = | 
					
						
							|  |  |  | 		clockevent_delta2ns(0xf, &iop_clockevent); | 
					
						
							|  |  |  | 	iop_clockevent.cpumask = cpumask_of(0); | 
					
						
							|  |  |  | 	clockevents_register_device(&iop_clockevent); | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Set up free-running clocksource timer 1. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-02-13 17:13:34 +01:00
										 |  |  | 	write_trr1(0xffffffff); | 
					
						
							| 
									
										
										
										
											2009-10-29 11:46:54 -07:00
										 |  |  | 	write_tcr1(0xffffffff); | 
					
						
							| 
									
										
										
										
											2007-02-13 17:13:34 +01:00
										 |  |  | 	write_tmr1(timer_ctl); | 
					
						
							| 
									
										
										
										
											2010-12-13 13:20:23 +00:00
										 |  |  | 	clocksource_register_hz(&iop_clocksource, tick_rate); | 
					
						
							| 
									
										
										
										
											2006-09-18 23:18:16 +01:00
										 |  |  | } |