mps2-timer.c (6209B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2015 ARM Limited 4 * 5 * Author: Vladimir Murzin <vladimir.murzin@arm.com> 6 */ 7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10#include <linux/clk.h> 11#include <linux/clockchips.h> 12#include <linux/clocksource.h> 13#include <linux/err.h> 14#include <linux/interrupt.h> 15#include <linux/io.h> 16#include <linux/irq.h> 17#include <linux/of_address.h> 18#include <linux/of.h> 19#include <linux/of_irq.h> 20#include <linux/sched_clock.h> 21#include <linux/slab.h> 22 23#define TIMER_CTRL 0x0 24#define TIMER_CTRL_ENABLE BIT(0) 25#define TIMER_CTRL_IE BIT(3) 26 27#define TIMER_VALUE 0x4 28#define TIMER_RELOAD 0x8 29#define TIMER_INT 0xc 30 31struct clockevent_mps2 { 32 void __iomem *reg; 33 u32 clock_count_per_tick; 34 struct clock_event_device clkevt; 35}; 36 37static void __iomem *sched_clock_base; 38 39static u64 notrace mps2_sched_read(void) 40{ 41 return ~readl_relaxed(sched_clock_base + TIMER_VALUE); 42} 43 44static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c) 45{ 46 return container_of(c, struct clockevent_mps2, clkevt); 47} 48 49static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset) 50{ 51 writel_relaxed(val, to_mps2_clkevt(c)->reg + offset); 52} 53 54static int mps2_timer_shutdown(struct clock_event_device *ce) 55{ 56 clockevent_mps2_writel(0, ce, TIMER_RELOAD); 57 clockevent_mps2_writel(0, ce, TIMER_CTRL); 58 59 return 0; 60} 61 62static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce) 63{ 64 clockevent_mps2_writel(next, ce, TIMER_VALUE); 65 clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL); 66 67 return 0; 68} 69 70static int mps2_timer_set_periodic(struct clock_event_device *ce) 71{ 72 u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick; 73 74 clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD); 75 clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE); 76 clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL); 77 78 return 0; 79} 80 81static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id) 82{ 83 struct clockevent_mps2 *ce = dev_id; 84 u32 status = readl_relaxed(ce->reg + TIMER_INT); 85 86 if (!status) { 87 pr_warn("spurious interrupt\n"); 88 return IRQ_NONE; 89 } 90 91 writel_relaxed(1, ce->reg + TIMER_INT); 92 93 ce->clkevt.event_handler(&ce->clkevt); 94 95 return IRQ_HANDLED; 96} 97 98static int __init mps2_clockevent_init(struct device_node *np) 99{ 100 void __iomem *base; 101 struct clk *clk = NULL; 102 struct clockevent_mps2 *ce; 103 u32 rate; 104 int irq, ret; 105 const char *name = "mps2-clkevt"; 106 107 ret = of_property_read_u32(np, "clock-frequency", &rate); 108 if (ret) { 109 clk = of_clk_get(np, 0); 110 if (IS_ERR(clk)) { 111 ret = PTR_ERR(clk); 112 pr_err("failed to get clock for clockevent: %d\n", ret); 113 goto out; 114 } 115 116 ret = clk_prepare_enable(clk); 117 if (ret) { 118 pr_err("failed to enable clock for clockevent: %d\n", ret); 119 goto out_clk_put; 120 } 121 122 rate = clk_get_rate(clk); 123 } 124 125 base = of_iomap(np, 0); 126 if (!base) { 127 ret = -EADDRNOTAVAIL; 128 pr_err("failed to map register for clockevent: %d\n", ret); 129 goto out_clk_disable; 130 } 131 132 irq = irq_of_parse_and_map(np, 0); 133 if (!irq) { 134 ret = -ENOENT; 135 pr_err("failed to get irq for clockevent: %d\n", ret); 136 goto out_iounmap; 137 } 138 139 ce = kzalloc(sizeof(*ce), GFP_KERNEL); 140 if (!ce) { 141 ret = -ENOMEM; 142 goto out_iounmap; 143 } 144 145 ce->reg = base; 146 ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ); 147 ce->clkevt.irq = irq; 148 ce->clkevt.name = name; 149 ce->clkevt.rating = 200; 150 ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 151 ce->clkevt.cpumask = cpu_possible_mask; 152 ce->clkevt.set_state_shutdown = mps2_timer_shutdown; 153 ce->clkevt.set_state_periodic = mps2_timer_set_periodic; 154 ce->clkevt.set_state_oneshot = mps2_timer_shutdown; 155 ce->clkevt.set_next_event = mps2_timer_set_next_event; 156 157 /* Ensure timer is disabled */ 158 writel_relaxed(0, base + TIMER_CTRL); 159 160 ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce); 161 if (ret) { 162 pr_err("failed to request irq for clockevent: %d\n", ret); 163 goto out_kfree; 164 } 165 166 clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff); 167 168 return 0; 169 170out_kfree: 171 kfree(ce); 172out_iounmap: 173 iounmap(base); 174out_clk_disable: 175 /* clk_{disable, unprepare, put}() can handle NULL as a parameter */ 176 clk_disable_unprepare(clk); 177out_clk_put: 178 clk_put(clk); 179out: 180 return ret; 181} 182 183static int __init mps2_clocksource_init(struct device_node *np) 184{ 185 void __iomem *base; 186 struct clk *clk = NULL; 187 u32 rate; 188 int ret; 189 const char *name = "mps2-clksrc"; 190 191 ret = of_property_read_u32(np, "clock-frequency", &rate); 192 if (ret) { 193 clk = of_clk_get(np, 0); 194 if (IS_ERR(clk)) { 195 ret = PTR_ERR(clk); 196 pr_err("failed to get clock for clocksource: %d\n", ret); 197 goto out; 198 } 199 200 ret = clk_prepare_enable(clk); 201 if (ret) { 202 pr_err("failed to enable clock for clocksource: %d\n", ret); 203 goto out_clk_put; 204 } 205 206 rate = clk_get_rate(clk); 207 } 208 209 base = of_iomap(np, 0); 210 if (!base) { 211 ret = -EADDRNOTAVAIL; 212 pr_err("failed to map register for clocksource: %d\n", ret); 213 goto out_clk_disable; 214 } 215 216 /* Ensure timer is disabled */ 217 writel_relaxed(0, base + TIMER_CTRL); 218 219 /* ... and set it up as free-running clocksource */ 220 writel_relaxed(0xffffffff, base + TIMER_VALUE); 221 writel_relaxed(0xffffffff, base + TIMER_RELOAD); 222 223 writel_relaxed(TIMER_CTRL_ENABLE, base + TIMER_CTRL); 224 225 ret = clocksource_mmio_init(base + TIMER_VALUE, name, 226 rate, 200, 32, 227 clocksource_mmio_readl_down); 228 if (ret) { 229 pr_err("failed to init clocksource: %d\n", ret); 230 goto out_iounmap; 231 } 232 233 sched_clock_base = base; 234 sched_clock_register(mps2_sched_read, 32, rate); 235 236 return 0; 237 238out_iounmap: 239 iounmap(base); 240out_clk_disable: 241 /* clk_{disable, unprepare, put}() can handle NULL as a parameter */ 242 clk_disable_unprepare(clk); 243out_clk_put: 244 clk_put(clk); 245out: 246 return ret; 247} 248 249static int __init mps2_timer_init(struct device_node *np) 250{ 251 static int has_clocksource, has_clockevent; 252 int ret; 253 254 if (!has_clocksource) { 255 ret = mps2_clocksource_init(np); 256 if (!ret) { 257 has_clocksource = 1; 258 return 0; 259 } 260 } 261 262 if (!has_clockevent) { 263 ret = mps2_clockevent_init(np); 264 if (!ret) { 265 has_clockevent = 1; 266 return 0; 267 } 268 } 269 270 return 0; 271} 272 273TIMER_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);