xref: /linux/arch/m68k/coldfire/pit.c (revision e5c86679d5e864947a52fb31e45a425dea3e7fa9)
1 /***************************************************************************/
2 
3 /*
4  *	pit.c -- Freescale ColdFire PIT timer. Currently this type of
5  *	         hardware timer only exists in the Freescale ColdFire
6  *		 5270/5271, 5282 and 5208 CPUs. No doubt newer ColdFire
7  *		 family members will probably use it too.
8  *
9  *	Copyright (C) 1999-2008, Greg Ungerer (gerg@snapgear.com)
10  *	Copyright (C) 2001-2004, SnapGear Inc. (www.snapgear.com)
11  */
12 
13 /***************************************************************************/
14 
15 #include <linux/kernel.h>
16 #include <linux/sched.h>
17 #include <linux/param.h>
18 #include <linux/init.h>
19 #include <linux/interrupt.h>
20 #include <linux/irq.h>
21 #include <linux/clockchips.h>
22 #include <asm/machdep.h>
23 #include <asm/io.h>
24 #include <asm/coldfire.h>
25 #include <asm/mcfpit.h>
26 #include <asm/mcfsim.h>
27 
28 /***************************************************************************/
29 
30 /*
31  *	By default use timer1 as the system clock timer.
32  */
33 #define	FREQ	((MCF_CLK / 2) / 64)
34 #define	TA(a)	(MCFPIT_BASE1 + (a))
35 #define PIT_CYCLES_PER_JIFFY (FREQ / HZ)
36 
37 static u32 pit_cnt;
38 
39 /*
40  * Initialize the PIT timer.
41  *
42  * This is also called after resume to bring the PIT into operation again.
43  */
44 
45 static int cf_pit_set_periodic(struct clock_event_device *evt)
46 {
47 	__raw_writew(MCFPIT_PCSR_DISABLE, TA(MCFPIT_PCSR));
48 	__raw_writew(PIT_CYCLES_PER_JIFFY, TA(MCFPIT_PMR));
49 	__raw_writew(MCFPIT_PCSR_EN | MCFPIT_PCSR_PIE |
50 		     MCFPIT_PCSR_OVW | MCFPIT_PCSR_RLD |
51 		     MCFPIT_PCSR_CLK64, TA(MCFPIT_PCSR));
52 	return 0;
53 }
54 
55 static int cf_pit_set_oneshot(struct clock_event_device *evt)
56 {
57 	__raw_writew(MCFPIT_PCSR_DISABLE, TA(MCFPIT_PCSR));
58 	__raw_writew(MCFPIT_PCSR_EN | MCFPIT_PCSR_PIE |
59 		     MCFPIT_PCSR_OVW | MCFPIT_PCSR_CLK64, TA(MCFPIT_PCSR));
60 	return 0;
61 }
62 
63 static int cf_pit_shutdown(struct clock_event_device *evt)
64 {
65 	__raw_writew(MCFPIT_PCSR_DISABLE, TA(MCFPIT_PCSR));
66 	return 0;
67 }
68 
69 /*
70  * Program the next event in oneshot mode
71  *
72  * Delta is given in PIT ticks
73  */
74 static int cf_pit_next_event(unsigned long delta,
75 		struct clock_event_device *evt)
76 {
77 	__raw_writew(delta, TA(MCFPIT_PMR));
78 	return 0;
79 }
80 
81 struct clock_event_device cf_pit_clockevent = {
82 	.name			= "pit",
83 	.features		= CLOCK_EVT_FEAT_PERIODIC |
84 				  CLOCK_EVT_FEAT_ONESHOT,
85 	.set_state_shutdown	= cf_pit_shutdown,
86 	.set_state_periodic	= cf_pit_set_periodic,
87 	.set_state_oneshot	= cf_pit_set_oneshot,
88 	.set_next_event		= cf_pit_next_event,
89 	.shift			= 32,
90 	.irq			= MCF_IRQ_PIT1,
91 };
92 
93 
94 
95 /***************************************************************************/
96 
97 static irqreturn_t pit_tick(int irq, void *dummy)
98 {
99 	struct clock_event_device *evt = &cf_pit_clockevent;
100 	u16 pcsr;
101 
102 	/* Reset the ColdFire timer */
103 	pcsr = __raw_readw(TA(MCFPIT_PCSR));
104 	__raw_writew(pcsr | MCFPIT_PCSR_PIF, TA(MCFPIT_PCSR));
105 
106 	pit_cnt += PIT_CYCLES_PER_JIFFY;
107 	evt->event_handler(evt);
108 	return IRQ_HANDLED;
109 }
110 
111 /***************************************************************************/
112 
113 static struct irqaction pit_irq = {
114 	.name	 = "timer",
115 	.flags	 = IRQF_TIMER,
116 	.handler = pit_tick,
117 };
118 
119 /***************************************************************************/
120 
121 static u64 pit_read_clk(struct clocksource *cs)
122 {
123 	unsigned long flags;
124 	u32 cycles;
125 	u16 pcntr;
126 
127 	local_irq_save(flags);
128 	pcntr = __raw_readw(TA(MCFPIT_PCNTR));
129 	cycles = pit_cnt;
130 	local_irq_restore(flags);
131 
132 	return cycles + PIT_CYCLES_PER_JIFFY - pcntr;
133 }
134 
135 /***************************************************************************/
136 
137 static struct clocksource pit_clk = {
138 	.name	= "pit",
139 	.rating	= 100,
140 	.read	= pit_read_clk,
141 	.mask	= CLOCKSOURCE_MASK(32),
142 };
143 
144 /***************************************************************************/
145 
146 void hw_timer_init(irq_handler_t handler)
147 {
148 	cf_pit_clockevent.cpumask = cpumask_of(smp_processor_id());
149 	cf_pit_clockevent.mult = div_sc(FREQ, NSEC_PER_SEC, 32);
150 	cf_pit_clockevent.max_delta_ns =
151 		clockevent_delta2ns(0xFFFF, &cf_pit_clockevent);
152 	cf_pit_clockevent.min_delta_ns =
153 		clockevent_delta2ns(0x3f, &cf_pit_clockevent);
154 	clockevents_register_device(&cf_pit_clockevent);
155 
156 	setup_irq(MCF_IRQ_PIT1, &pit_irq);
157 
158 	clocksource_register_hz(&pit_clk, FREQ);
159 }
160 
161 /***************************************************************************/
162