1 // SPDX-License-Identifier: GPL-2.0
3 * Renesas Timer Support - OSTM
5 * Copyright (C) 2017 Renesas Electronics America, Inc.
6 * Copyright (C) 2017 Chris Brandt
10 #include <linux/clockchips.h>
11 #include <linux/interrupt.h>
12 #include <linux/sched_clock.h>
13 #include <linux/slab.h>
18 * The OSTM contains independent channels.
19 * The first OSTM channel probed will be set up as a free running
20 * clocksource. Additionally we will use this clocksource for the system
21 * schedule timer sched_clock().
23 * The second (or more) channel probed will be set up as an interrupt
27 static void __iomem
*system_clock
; /* For sched_clock() */
30 #define OSTM_CMP 0x000 /* RW,32 */
31 #define OSTM_CNT 0x004 /* R,32 */
32 #define OSTM_TE 0x010 /* R,8 */
33 #define OSTM_TS 0x014 /* W,8 */
34 #define OSTM_TT 0x018 /* W,8 */
35 #define OSTM_CTL 0x020 /* RW,8 */
40 #define CTL_PERIODIC 0x00
41 #define CTL_ONESHOT 0x02
42 #define CTL_FREERUN 0x02
44 static void ostm_timer_stop(struct timer_of
*to
)
46 if (readb(timer_of_base(to
) + OSTM_TE
) & TE
) {
47 writeb(TT
, timer_of_base(to
) + OSTM_TT
);
50 * Read back the register simply to confirm the write operation
51 * has completed since I/O writes can sometimes get queued by
52 * the bus architecture.
54 while (readb(timer_of_base(to
) + OSTM_TE
) & TE
)
59 static int __init
ostm_init_clksrc(struct timer_of
*to
)
63 writel(0, timer_of_base(to
) + OSTM_CMP
);
64 writeb(CTL_FREERUN
, timer_of_base(to
) + OSTM_CTL
);
65 writeb(TS
, timer_of_base(to
) + OSTM_TS
);
67 return clocksource_mmio_init(timer_of_base(to
) + OSTM_CNT
,
68 to
->np
->full_name
, timer_of_rate(to
), 300,
69 32, clocksource_mmio_readl_up
);
72 static u64 notrace
ostm_read_sched_clock(void)
74 return readl(system_clock
);
77 static void __init
ostm_init_sched_clock(struct timer_of
*to
)
79 system_clock
= timer_of_base(to
) + OSTM_CNT
;
80 sched_clock_register(ostm_read_sched_clock
, 32, timer_of_rate(to
));
83 static int ostm_clock_event_next(unsigned long delta
,
84 struct clock_event_device
*ced
)
86 struct timer_of
*to
= to_timer_of(ced
);
90 writel(delta
, timer_of_base(to
) + OSTM_CMP
);
91 writeb(CTL_ONESHOT
, timer_of_base(to
) + OSTM_CTL
);
92 writeb(TS
, timer_of_base(to
) + OSTM_TS
);
97 static int ostm_shutdown(struct clock_event_device
*ced
)
99 struct timer_of
*to
= to_timer_of(ced
);
105 static int ostm_set_periodic(struct clock_event_device
*ced
)
107 struct timer_of
*to
= to_timer_of(ced
);
109 if (clockevent_state_oneshot(ced
) || clockevent_state_periodic(ced
))
112 writel(timer_of_period(to
) - 1, timer_of_base(to
) + OSTM_CMP
);
113 writeb(CTL_PERIODIC
, timer_of_base(to
) + OSTM_CTL
);
114 writeb(TS
, timer_of_base(to
) + OSTM_TS
);
119 static int ostm_set_oneshot(struct clock_event_device
*ced
)
121 struct timer_of
*to
= to_timer_of(ced
);
128 static irqreturn_t
ostm_timer_interrupt(int irq
, void *dev_id
)
130 struct clock_event_device
*ced
= dev_id
;
132 if (clockevent_state_oneshot(ced
))
133 ostm_timer_stop(to_timer_of(ced
));
135 /* notify clockevent layer */
136 if (ced
->event_handler
)
137 ced
->event_handler(ced
);
142 static int __init
ostm_init_clkevt(struct timer_of
*to
)
144 struct clock_event_device
*ced
= &to
->clkevt
;
146 ced
->features
= CLOCK_EVT_FEAT_ONESHOT
| CLOCK_EVT_FEAT_PERIODIC
;
147 ced
->set_state_shutdown
= ostm_shutdown
;
148 ced
->set_state_periodic
= ostm_set_periodic
;
149 ced
->set_state_oneshot
= ostm_set_oneshot
;
150 ced
->set_next_event
= ostm_clock_event_next
;
153 ced
->cpumask
= cpumask_of(0);
154 clockevents_config_and_register(ced
, timer_of_rate(to
), 0xf,
160 static int __init
ostm_init(struct device_node
*np
)
165 to
= kzalloc(sizeof(*to
), GFP_KERNEL
);
169 to
->flags
= TIMER_OF_BASE
| TIMER_OF_CLOCK
;
172 * clock sources don't use interrupts, clock events do
174 to
->flags
|= TIMER_OF_IRQ
;
175 to
->of_irq
.flags
= IRQF_TIMER
| IRQF_IRQPOLL
;
176 to
->of_irq
.handler
= ostm_timer_interrupt
;
179 ret
= timer_of_init(np
, to
);
184 * First probed device will be used as system clocksource. Any
185 * additional devices will be used as clock events.
188 ret
= ostm_init_clksrc(to
);
192 ostm_init_sched_clock(to
);
193 pr_info("%pOF: used for clocksource\n", np
);
195 ret
= ostm_init_clkevt(to
);
199 pr_info("%pOF: used for clock events\n", np
);
205 timer_of_cleanup(to
);
211 TIMER_OF_DECLARE(ostm
, "renesas,ostm", ostm_init
);