2 * linux/drivers/clocksource/zevio-timer.c
4 * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2, as
8 * published by the Free Software Foundation.
13 #include <linux/irq.h>
15 #include <linux/of_address.h>
16 #include <linux/of_irq.h>
17 #include <linux/clk.h>
18 #include <linux/clockchips.h>
19 #include <linux/cpumask.h>
20 #include <linux/interrupt.h>
21 #include <linux/slab.h>
23 #define IO_CURRENT_VAL 0x00
24 #define IO_DIVIDER 0x04
25 #define IO_CONTROL 0x08
27 #define IO_TIMER1 0x00
28 #define IO_TIMER2 0x0C
30 #define IO_MATCH_BEGIN 0x18
31 #define IO_MATCH(x) (IO_MATCH_BEGIN + ((x) << 2))
33 #define IO_INTR_STS 0x00
34 #define IO_INTR_ACK 0x00
35 #define IO_INTR_MSK 0x04
37 #define CNTL_STOP_TIMER (1 << 4)
38 #define CNTL_RUN_TIMER (0 << 4)
40 #define CNTL_INC (1 << 3)
41 #define CNTL_DEC (0 << 3)
44 #define CNTL_MATCH(x) ((x) + 1)
45 #define CNTL_FOREVER 7
47 /* There are 6 match registers but we only use one. */
50 #define TIMER_INTR_MSK (1 << (TIMER_MATCH))
51 #define TIMER_INTR_ALL 0x3F
55 void __iomem
*timer1
, *timer2
;
56 void __iomem
*interrupt_regs
;
59 struct clock_event_device clkevt
;
60 struct irqaction clkevt_irq
;
62 char clocksource_name
[64];
63 char clockevent_name
[64];
66 static int zevio_timer_set_event(unsigned long delta
,
67 struct clock_event_device
*dev
)
69 struct zevio_timer
*timer
= container_of(dev
, struct zevio_timer
,
72 writel(delta
, timer
->timer1
+ IO_CURRENT_VAL
);
73 writel(CNTL_RUN_TIMER
| CNTL_DEC
| CNTL_MATCH(TIMER_MATCH
),
74 timer
->timer1
+ IO_CONTROL
);
79 static void zevio_timer_set_mode(enum clock_event_mode mode
,
80 struct clock_event_device
*dev
)
82 struct zevio_timer
*timer
= container_of(dev
, struct zevio_timer
,
86 case CLOCK_EVT_MODE_RESUME
:
87 case CLOCK_EVT_MODE_ONESHOT
:
88 /* Enable timer interrupts */
89 writel(TIMER_INTR_MSK
, timer
->interrupt_regs
+ IO_INTR_MSK
);
90 writel(TIMER_INTR_ALL
, timer
->interrupt_regs
+ IO_INTR_ACK
);
92 case CLOCK_EVT_MODE_SHUTDOWN
:
93 case CLOCK_EVT_MODE_UNUSED
:
94 /* Disable timer interrupts */
95 writel(0, timer
->interrupt_regs
+ IO_INTR_MSK
);
96 writel(TIMER_INTR_ALL
, timer
->interrupt_regs
+ IO_INTR_ACK
);
98 writel(CNTL_STOP_TIMER
, timer
->timer1
+ IO_CONTROL
);
100 case CLOCK_EVT_MODE_PERIODIC
:
107 static irqreturn_t
zevio_timer_interrupt(int irq
, void *dev_id
)
109 struct zevio_timer
*timer
= dev_id
;
112 intr
= readl(timer
->interrupt_regs
+ IO_INTR_ACK
);
113 if (!(intr
& TIMER_INTR_MSK
))
116 writel(TIMER_INTR_MSK
, timer
->interrupt_regs
+ IO_INTR_ACK
);
117 writel(CNTL_STOP_TIMER
, timer
->timer1
+ IO_CONTROL
);
119 if (timer
->clkevt
.event_handler
)
120 timer
->clkevt
.event_handler(&timer
->clkevt
);
125 static int __init
zevio_timer_add(struct device_node
*node
)
127 struct zevio_timer
*timer
;
131 timer
= kzalloc(sizeof(*timer
), GFP_KERNEL
);
135 timer
->base
= of_iomap(node
, 0);
140 timer
->timer1
= timer
->base
+ IO_TIMER1
;
141 timer
->timer2
= timer
->base
+ IO_TIMER2
;
143 timer
->clk
= of_clk_get(node
, 0);
144 if (IS_ERR(timer
->clk
)) {
145 ret
= PTR_ERR(timer
->clk
);
146 pr_err("Timer clock not found! (error %d)\n", ret
);
150 timer
->interrupt_regs
= of_iomap(node
, 1);
151 irqnr
= irq_of_parse_and_map(node
, 0);
153 of_address_to_resource(node
, 0, &res
);
154 scnprintf(timer
->clocksource_name
, sizeof(timer
->clocksource_name
),
155 "%llx.%s_clocksource",
156 (unsigned long long)res
.start
, node
->name
);
158 scnprintf(timer
->clockevent_name
, sizeof(timer
->clockevent_name
),
159 "%llx.%s_clockevent",
160 (unsigned long long)res
.start
, node
->name
);
162 if (timer
->interrupt_regs
&& irqnr
) {
163 timer
->clkevt
.name
= timer
->clockevent_name
;
164 timer
->clkevt
.set_next_event
= zevio_timer_set_event
;
165 timer
->clkevt
.set_mode
= zevio_timer_set_mode
;
166 timer
->clkevt
.rating
= 200;
167 timer
->clkevt
.cpumask
= cpu_all_mask
;
168 timer
->clkevt
.features
= CLOCK_EVT_FEAT_ONESHOT
;
169 timer
->clkevt
.irq
= irqnr
;
171 writel(CNTL_STOP_TIMER
, timer
->timer1
+ IO_CONTROL
);
172 writel(0, timer
->timer1
+ IO_DIVIDER
);
174 /* Start with timer interrupts disabled */
175 writel(0, timer
->interrupt_regs
+ IO_INTR_MSK
);
176 writel(TIMER_INTR_ALL
, timer
->interrupt_regs
+ IO_INTR_ACK
);
178 /* Interrupt to occur when timer value matches 0 */
179 writel(0, timer
->base
+ IO_MATCH(TIMER_MATCH
));
181 timer
->clkevt_irq
.name
= timer
->clockevent_name
;
182 timer
->clkevt_irq
.handler
= zevio_timer_interrupt
;
183 timer
->clkevt_irq
.dev_id
= timer
;
184 timer
->clkevt_irq
.flags
= IRQF_TIMER
| IRQF_IRQPOLL
;
186 setup_irq(irqnr
, &timer
->clkevt_irq
);
188 clockevents_config_and_register(&timer
->clkevt
,
189 clk_get_rate(timer
->clk
), 0x0001, 0xffff);
190 pr_info("Added %s as clockevent\n", timer
->clockevent_name
);
193 writel(CNTL_STOP_TIMER
, timer
->timer2
+ IO_CONTROL
);
194 writel(0, timer
->timer2
+ IO_CURRENT_VAL
);
195 writel(0, timer
->timer2
+ IO_DIVIDER
);
196 writel(CNTL_RUN_TIMER
| CNTL_FOREVER
| CNTL_INC
,
197 timer
->timer2
+ IO_CONTROL
);
199 clocksource_mmio_init(timer
->timer2
+ IO_CURRENT_VAL
,
200 timer
->clocksource_name
,
201 clk_get_rate(timer
->clk
),
203 clocksource_mmio_readw_up
);
205 pr_info("Added %s as clocksource\n", timer
->clocksource_name
);
209 iounmap(timer
->base
);
215 static void __init
zevio_timer_init(struct device_node
*node
)
217 BUG_ON(zevio_timer_add(node
));
220 CLOCKSOURCE_OF_DECLARE(zevio_timer
, "lsi,zevio-timer", zevio_timer_init
);