1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Renesas Timer Support - OSTM 4 * 5 * Copyright (C) 2017 Renesas Electronics America, Inc. 6 * Copyright (C) 2017 Chris Brandt 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/clockchips.h> 11 #include <linux/interrupt.h> 12 #include <linux/platform_device.h> 13 #include <linux/reset.h> 14 #include <linux/sched_clock.h> 15 #include <linux/slab.h> 16 17 #include "timer-of.h" 18 19 /* 20 * The OSTM contains independent channels. 21 * The first OSTM channel probed will be set up as a free running 22 * clocksource. Additionally we will use this clocksource for the system 23 * schedule timer sched_clock(). 24 * 25 * The second (or more) channel probed will be set up as an interrupt 26 * driven clock event. 27 */ 28 29 static void __iomem *system_clock; /* For sched_clock() */ 30 31 /* OSTM REGISTERS */ 32 #define OSTM_CMP 0x000 /* RW,32 */ 33 #define OSTM_CNT 0x004 /* R,32 */ 34 #define OSTM_TE 0x010 /* R,8 */ 35 #define OSTM_TS 0x014 /* W,8 */ 36 #define OSTM_TT 0x018 /* W,8 */ 37 #define OSTM_CTL 0x020 /* RW,8 */ 38 39 #define TE 0x01 40 #define TS 0x01 41 #define TT 0x01 42 #define CTL_PERIODIC 0x00 43 #define CTL_ONESHOT 0x02 44 #define CTL_FREERUN 0x02 45 46 static void ostm_timer_stop(struct timer_of *to) 47 { 48 if (readb(timer_of_base(to) + OSTM_TE) & TE) { 49 writeb(TT, timer_of_base(to) + OSTM_TT); 50 51 /* 52 * Read back the register simply to confirm the write operation 53 * has completed since I/O writes can sometimes get queued by 54 * the bus architecture. 55 */ 56 while (readb(timer_of_base(to) + OSTM_TE) & TE) 57 ; 58 } 59 } 60 61 static int __init ostm_init_clksrc(struct timer_of *to) 62 { 63 ostm_timer_stop(to); 64 65 writel(0, timer_of_base(to) + OSTM_CMP); 66 writeb(CTL_FREERUN, timer_of_base(to) + OSTM_CTL); 67 writeb(TS, timer_of_base(to) + OSTM_TS); 68 69 return clocksource_mmio_init(timer_of_base(to) + OSTM_CNT, 70 to->np->full_name, timer_of_rate(to), 300, 71 32, clocksource_mmio_readl_up); 72 } 73 74 static u64 notrace ostm_read_sched_clock(void) 75 { 76 return readl(system_clock); 77 } 78 79 static void __init ostm_init_sched_clock(struct timer_of *to) 80 { 81 system_clock = timer_of_base(to) + OSTM_CNT; 82 sched_clock_register(ostm_read_sched_clock, 32, timer_of_rate(to)); 83 } 84 85 static int ostm_clock_event_next(unsigned long delta, 86 struct clock_event_device *ced) 87 { 88 struct timer_of *to = to_timer_of(ced); 89 90 ostm_timer_stop(to); 91 92 writel(delta, timer_of_base(to) + OSTM_CMP); 93 writeb(CTL_ONESHOT, timer_of_base(to) + OSTM_CTL); 94 writeb(TS, timer_of_base(to) + OSTM_TS); 95 96 return 0; 97 } 98 99 static int ostm_shutdown(struct clock_event_device *ced) 100 { 101 struct timer_of *to = to_timer_of(ced); 102 103 ostm_timer_stop(to); 104 105 return 0; 106 } 107 static int ostm_set_periodic(struct clock_event_device *ced) 108 { 109 struct timer_of *to = to_timer_of(ced); 110 111 if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced)) 112 ostm_timer_stop(to); 113 114 writel(timer_of_period(to) - 1, timer_of_base(to) + OSTM_CMP); 115 writeb(CTL_PERIODIC, timer_of_base(to) + OSTM_CTL); 116 writeb(TS, timer_of_base(to) + OSTM_TS); 117 118 return 0; 119 } 120 121 static int ostm_set_oneshot(struct clock_event_device *ced) 122 { 123 struct timer_of *to = to_timer_of(ced); 124 125 ostm_timer_stop(to); 126 127 return 0; 128 } 129 130 static irqreturn_t ostm_timer_interrupt(int irq, void *dev_id) 131 { 132 struct clock_event_device *ced = dev_id; 133 134 if (clockevent_state_oneshot(ced)) 135 ostm_timer_stop(to_timer_of(ced)); 136 137 /* notify clockevent layer */ 138 if (ced->event_handler) 139 ced->event_handler(ced); 140 141 return IRQ_HANDLED; 142 } 143 144 static int __init ostm_init_clkevt(struct timer_of *to) 145 { 146 struct clock_event_device *ced = &to->clkevt; 147 148 ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC; 149 ced->set_state_shutdown = ostm_shutdown; 150 ced->set_state_periodic = ostm_set_periodic; 151 ced->set_state_oneshot = ostm_set_oneshot; 152 ced->set_next_event = ostm_clock_event_next; 153 ced->shift = 32; 154 ced->rating = 300; 155 ced->cpumask = cpumask_of(0); 156 clockevents_config_and_register(ced, timer_of_rate(to), 0xf, 157 0xffffffff); 158 159 return 0; 160 } 161 162 static int __init ostm_init(struct device_node *np) 163 { 164 struct reset_control *rstc; 165 struct timer_of *to; 166 int ret; 167 168 to = kzalloc(sizeof(*to), GFP_KERNEL); 169 if (!to) 170 return -ENOMEM; 171 172 rstc = of_reset_control_get_optional_exclusive(np, NULL); 173 if (IS_ERR(rstc)) { 174 ret = PTR_ERR(rstc); 175 goto err_free; 176 } 177 178 reset_control_deassert(rstc); 179 180 to->flags = TIMER_OF_BASE | TIMER_OF_CLOCK; 181 if (system_clock) { 182 /* 183 * clock sources don't use interrupts, clock events do 184 */ 185 to->flags |= TIMER_OF_IRQ; 186 to->of_irq.flags = IRQF_TIMER | IRQF_IRQPOLL; 187 to->of_irq.handler = ostm_timer_interrupt; 188 } 189 190 ret = timer_of_init(np, to); 191 if (ret) 192 goto err_reset; 193 194 /* 195 * First probed device will be used as system clocksource. Any 196 * additional devices will be used as clock events. 197 */ 198 if (!system_clock) { 199 ret = ostm_init_clksrc(to); 200 if (ret) 201 goto err_cleanup; 202 203 ostm_init_sched_clock(to); 204 pr_info("%pOF: used for clocksource\n", np); 205 } else { 206 ret = ostm_init_clkevt(to); 207 if (ret) 208 goto err_cleanup; 209 210 pr_info("%pOF: used for clock events\n", np); 211 } 212 213 return 0; 214 215 err_cleanup: 216 timer_of_cleanup(to); 217 err_reset: 218 reset_control_assert(rstc); 219 reset_control_put(rstc); 220 err_free: 221 kfree(to); 222 return ret; 223 } 224 225 TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init); 226 227 #ifdef CONFIG_ARCH_R9A07G044 228 static int __init ostm_probe(struct platform_device *pdev) 229 { 230 struct device *dev = &pdev->dev; 231 232 return ostm_init(dev->of_node); 233 } 234 235 static const struct of_device_id ostm_of_table[] = { 236 { .compatible = "renesas,ostm", }, 237 { /* sentinel */ } 238 }; 239 240 static struct platform_driver ostm_device_driver = { 241 .driver = { 242 .name = "renesas_ostm", 243 .of_match_table = of_match_ptr(ostm_of_table), 244 .suppress_bind_attrs = true, 245 }, 246 }; 247 builtin_platform_driver_probe(ostm_device_driver, ostm_probe); 248 #endif 249