1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public |
3 | * License. See the file "COPYING" in the main directory of this archive |
4 | * for more details. |
5 | * |
6 | * Based on linux/arch/mips/kernel/cevt-r4k.c, |
7 | * linux/arch/mips/jmr3927/rbhma3100/setup.c |
8 | * |
9 | * Copyright 2001 MontaVista Software Inc. |
10 | * Copyright (C) 2000-2001 Toshiba Corporation |
11 | * Copyright (C) 2007 MIPS Technologies, Inc. |
12 | * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org> |
13 | */ |
14 | #include <linux/init.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/irq.h> |
17 | #include <linux/sched_clock.h> |
18 | #include <asm/time.h> |
19 | #include <asm/txx9tmr.h> |
20 | |
21 | #define TCR_BASE (TXx9_TMTCR_CCDE | TXx9_TMTCR_CRE | TXx9_TMTCR_TMODE_ITVL) |
22 | #define TIMER_CCD 0 /* 1/2 */ |
23 | #define TIMER_CLK(imclk) ((imclk) / (2 << TIMER_CCD)) |
24 | |
25 | struct txx9_clocksource { |
26 | struct clocksource cs; |
27 | struct txx9_tmr_reg __iomem *tmrptr; |
28 | }; |
29 | |
30 | static u64 txx9_cs_read(struct clocksource *cs) |
31 | { |
32 | struct txx9_clocksource *txx9_cs = |
33 | container_of(cs, struct txx9_clocksource, cs); |
34 | return __raw_readl(addr: &txx9_cs->tmrptr->trr); |
35 | } |
36 | |
37 | /* Use 1 bit smaller width to use full bits in that width */ |
38 | #define TXX9_CLOCKSOURCE_BITS (TXX9_TIMER_BITS - 1) |
39 | |
40 | static struct txx9_clocksource txx9_clocksource = { |
41 | .cs = { |
42 | .name = "TXx9" , |
43 | .rating = 200, |
44 | .read = txx9_cs_read, |
45 | .mask = CLOCKSOURCE_MASK(TXX9_CLOCKSOURCE_BITS), |
46 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
47 | }, |
48 | }; |
49 | |
50 | static u64 notrace txx9_read_sched_clock(void) |
51 | { |
52 | return __raw_readl(addr: &txx9_clocksource.tmrptr->trr); |
53 | } |
54 | |
55 | void __init txx9_clocksource_init(unsigned long baseaddr, |
56 | unsigned int imbusclk) |
57 | { |
58 | struct txx9_tmr_reg __iomem *tmrptr; |
59 | |
60 | clocksource_register_hz(cs: &txx9_clocksource.cs, TIMER_CLK(imbusclk)); |
61 | |
62 | tmrptr = ioremap(baseaddr, sizeof(struct txx9_tmr_reg)); |
63 | __raw_writel(TCR_BASE, &tmrptr->tcr); |
64 | __raw_writel(val: 0, addr: &tmrptr->tisr); |
65 | __raw_writel(TIMER_CCD, addr: &tmrptr->ccdr); |
66 | __raw_writel(val: TXx9_TMITMR_TZCE, addr: &tmrptr->itmr); |
67 | __raw_writel(1 << TXX9_CLOCKSOURCE_BITS, &tmrptr->cpra); |
68 | __raw_writel(TCR_BASE | TXx9_TMTCR_TCE, &tmrptr->tcr); |
69 | txx9_clocksource.tmrptr = tmrptr; |
70 | |
71 | sched_clock_register(txx9_read_sched_clock, TXX9_CLOCKSOURCE_BITS, |
72 | TIMER_CLK(imbusclk)); |
73 | } |
74 | |
75 | struct txx9_clock_event_device { |
76 | struct clock_event_device cd; |
77 | struct txx9_tmr_reg __iomem *tmrptr; |
78 | }; |
79 | |
80 | static void txx9tmr_stop_and_clear(struct txx9_tmr_reg __iomem *tmrptr) |
81 | { |
82 | /* stop and reset counter */ |
83 | __raw_writel(TCR_BASE, &tmrptr->tcr); |
84 | /* clear pending interrupt */ |
85 | __raw_writel(val: 0, addr: &tmrptr->tisr); |
86 | } |
87 | |
88 | static int txx9tmr_set_state_periodic(struct clock_event_device *evt) |
89 | { |
90 | struct txx9_clock_event_device *txx9_cd = |
91 | container_of(evt, struct txx9_clock_event_device, cd); |
92 | struct txx9_tmr_reg __iomem *tmrptr = txx9_cd->tmrptr; |
93 | |
94 | txx9tmr_stop_and_clear(tmrptr); |
95 | |
96 | __raw_writel(val: TXx9_TMITMR_TIIE | TXx9_TMITMR_TZCE, addr: &tmrptr->itmr); |
97 | /* start timer */ |
98 | __raw_writel(val: ((u64)(NSEC_PER_SEC / HZ) * evt->mult) >> evt->shift, |
99 | addr: &tmrptr->cpra); |
100 | __raw_writel(TCR_BASE | TXx9_TMTCR_TCE, &tmrptr->tcr); |
101 | return 0; |
102 | } |
103 | |
104 | static int txx9tmr_set_state_oneshot(struct clock_event_device *evt) |
105 | { |
106 | struct txx9_clock_event_device *txx9_cd = |
107 | container_of(evt, struct txx9_clock_event_device, cd); |
108 | struct txx9_tmr_reg __iomem *tmrptr = txx9_cd->tmrptr; |
109 | |
110 | txx9tmr_stop_and_clear(tmrptr); |
111 | __raw_writel(val: TXx9_TMITMR_TIIE, addr: &tmrptr->itmr); |
112 | return 0; |
113 | } |
114 | |
115 | static int txx9tmr_set_state_shutdown(struct clock_event_device *evt) |
116 | { |
117 | struct txx9_clock_event_device *txx9_cd = |
118 | container_of(evt, struct txx9_clock_event_device, cd); |
119 | struct txx9_tmr_reg __iomem *tmrptr = txx9_cd->tmrptr; |
120 | |
121 | txx9tmr_stop_and_clear(tmrptr); |
122 | __raw_writel(val: 0, addr: &tmrptr->itmr); |
123 | return 0; |
124 | } |
125 | |
126 | static int txx9tmr_tick_resume(struct clock_event_device *evt) |
127 | { |
128 | struct txx9_clock_event_device *txx9_cd = |
129 | container_of(evt, struct txx9_clock_event_device, cd); |
130 | struct txx9_tmr_reg __iomem *tmrptr = txx9_cd->tmrptr; |
131 | |
132 | txx9tmr_stop_and_clear(tmrptr); |
133 | __raw_writel(TIMER_CCD, addr: &tmrptr->ccdr); |
134 | __raw_writel(val: 0, addr: &tmrptr->itmr); |
135 | return 0; |
136 | } |
137 | |
138 | static int txx9tmr_set_next_event(unsigned long delta, |
139 | struct clock_event_device *evt) |
140 | { |
141 | struct txx9_clock_event_device *txx9_cd = |
142 | container_of(evt, struct txx9_clock_event_device, cd); |
143 | struct txx9_tmr_reg __iomem *tmrptr = txx9_cd->tmrptr; |
144 | |
145 | txx9tmr_stop_and_clear(tmrptr); |
146 | /* start timer */ |
147 | __raw_writel(val: delta, addr: &tmrptr->cpra); |
148 | __raw_writel(TCR_BASE | TXx9_TMTCR_TCE, &tmrptr->tcr); |
149 | return 0; |
150 | } |
151 | |
152 | static struct txx9_clock_event_device txx9_clock_event_device = { |
153 | .cd = { |
154 | .name = "TXx9" , |
155 | .features = CLOCK_EVT_FEAT_PERIODIC | |
156 | CLOCK_EVT_FEAT_ONESHOT, |
157 | .rating = 200, |
158 | .set_state_shutdown = txx9tmr_set_state_shutdown, |
159 | .set_state_periodic = txx9tmr_set_state_periodic, |
160 | .set_state_oneshot = txx9tmr_set_state_oneshot, |
161 | .tick_resume = txx9tmr_tick_resume, |
162 | .set_next_event = txx9tmr_set_next_event, |
163 | }, |
164 | }; |
165 | |
166 | static irqreturn_t txx9tmr_interrupt(int irq, void *dev_id) |
167 | { |
168 | struct txx9_clock_event_device *txx9_cd = dev_id; |
169 | struct clock_event_device *cd = &txx9_cd->cd; |
170 | struct txx9_tmr_reg __iomem *tmrptr = txx9_cd->tmrptr; |
171 | |
172 | __raw_writel(val: 0, addr: &tmrptr->tisr); /* ack interrupt */ |
173 | cd->event_handler(cd); |
174 | return IRQ_HANDLED; |
175 | } |
176 | |
177 | void __init txx9_clockevent_init(unsigned long baseaddr, int irq, |
178 | unsigned int imbusclk) |
179 | { |
180 | struct clock_event_device *cd = &txx9_clock_event_device.cd; |
181 | struct txx9_tmr_reg __iomem *tmrptr; |
182 | |
183 | tmrptr = ioremap(baseaddr, sizeof(struct txx9_tmr_reg)); |
184 | txx9tmr_stop_and_clear(tmrptr); |
185 | __raw_writel(TIMER_CCD, addr: &tmrptr->ccdr); |
186 | __raw_writel(val: 0, addr: &tmrptr->itmr); |
187 | txx9_clock_event_device.tmrptr = tmrptr; |
188 | |
189 | clockevent_set_clock(cd, TIMER_CLK(imbusclk)); |
190 | cd->max_delta_ns = |
191 | clockevent_delta2ns(0xffffffff >> (32 - TXX9_TIMER_BITS), cd); |
192 | cd->max_delta_ticks = 0xffffffff >> (32 - TXX9_TIMER_BITS); |
193 | cd->min_delta_ns = clockevent_delta2ns(0xf, cd); |
194 | cd->min_delta_ticks = 0xf; |
195 | cd->irq = irq; |
196 | cd->cpumask = cpumask_of(0); |
197 | clockevents_register_device(cd); |
198 | if (request_irq(irq, handler: txx9tmr_interrupt, IRQF_PERCPU | IRQF_TIMER, |
199 | name: "txx9tmr" , dev: &txx9_clock_event_device)) |
200 | pr_err("Failed to request irq %d (txx9tmr)\n" , irq); |
201 | printk(KERN_INFO "TXx9: clockevent device at 0x%lx, irq %d\n" , |
202 | baseaddr, irq); |
203 | } |
204 | |
205 | void __init txx9_tmr_init(unsigned long baseaddr) |
206 | { |
207 | struct txx9_tmr_reg __iomem *tmrptr; |
208 | |
209 | tmrptr = ioremap(baseaddr, sizeof(struct txx9_tmr_reg)); |
210 | /* Start once to make CounterResetEnable effective */ |
211 | __raw_writel(val: TXx9_TMTCR_CRE | TXx9_TMTCR_TCE, addr: &tmrptr->tcr); |
212 | /* Stop and reset the counter */ |
213 | __raw_writel(val: TXx9_TMTCR_CRE, addr: &tmrptr->tcr); |
214 | __raw_writel(val: 0, addr: &tmrptr->tisr); |
215 | __raw_writel(val: 0xffffffff, addr: &tmrptr->cpra); |
216 | __raw_writel(val: 0, addr: &tmrptr->itmr); |
217 | __raw_writel(val: 0, addr: &tmrptr->ccdr); |
218 | __raw_writel(val: 0, addr: &tmrptr->pgmr); |
219 | iounmap(addr: tmrptr); |
220 | } |
221 | |