208 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// Copyright 2017-2019 NXP4 5#include <linux/interrupt.h>6#include <linux/clockchips.h>7#include <linux/slab.h>8 9#include "timer-of.h"10 11#define CMP_OFFSET 0x1000012#define RD_OFFSET 0x2000013 14#define CNTCV_LO 0x815#define CNTCV_HI 0xc16#define CMPCV_LO (CMP_OFFSET + 0x20)17#define CMPCV_HI (CMP_OFFSET + 0x24)18#define CMPCR (CMP_OFFSET + 0x2c)19#define CNTCV_LO_IMX95 (RD_OFFSET + 0x8)20#define CNTCV_HI_IMX95 (RD_OFFSET + 0xc)21 22#define SYS_CTR_EN 0x123#define SYS_CTR_IRQ_MASK 0x224 25#define SYS_CTR_CLK_DIV 0x326 27struct sysctr_private {28 u32 cmpcr;29 u32 lo_off;30 u32 hi_off;31};32 33static void sysctr_timer_enable(struct clock_event_device *evt, bool enable)34{35 struct timer_of *to = to_timer_of(evt);36 struct sysctr_private *priv = to->private_data;37 void __iomem *base = timer_of_base(to);38 39 writel(enable ? priv->cmpcr | SYS_CTR_EN : priv->cmpcr, base + CMPCR);40}41 42static void sysctr_irq_acknowledge(struct clock_event_device *evt)43{44 /*45 * clear the enable bit(EN =0) will clear46 * the status bit(ISTAT = 0), then the interrupt47 * signal will be negated(acknowledged).48 */49 sysctr_timer_enable(evt, false);50}51 52static inline u64 sysctr_read_counter(struct clock_event_device *evt)53{54 struct timer_of *to = to_timer_of(evt);55 struct sysctr_private *priv = to->private_data;56 void __iomem *base = timer_of_base(to);57 u32 cnt_hi, tmp_hi, cnt_lo;58 59 do {60 cnt_hi = readl_relaxed(base + priv->hi_off);61 cnt_lo = readl_relaxed(base + priv->lo_off);62 tmp_hi = readl_relaxed(base + priv->hi_off);63 } while (tmp_hi != cnt_hi);64 65 return ((u64) cnt_hi << 32) | cnt_lo;66}67 68static int sysctr_set_next_event(unsigned long delta,69 struct clock_event_device *evt)70{71 struct timer_of *to = to_timer_of(evt);72 void __iomem *base = timer_of_base(to);73 u32 cmp_hi, cmp_lo;74 u64 next;75 76 sysctr_timer_enable(evt, false);77 78 next = sysctr_read_counter(evt);79 80 next += delta;81 82 cmp_hi = (next >> 32) & 0x00fffff;83 cmp_lo = next & 0xffffffff;84 85 writel_relaxed(cmp_hi, base + CMPCV_HI);86 writel_relaxed(cmp_lo, base + CMPCV_LO);87 88 sysctr_timer_enable(evt, true);89 90 return 0;91}92 93static int sysctr_set_state_oneshot(struct clock_event_device *evt)94{95 return 0;96}97 98static int sysctr_set_state_shutdown(struct clock_event_device *evt)99{100 sysctr_timer_enable(evt, false);101 102 return 0;103}104 105static irqreturn_t sysctr_timer_interrupt(int irq, void *dev_id)106{107 struct clock_event_device *evt = dev_id;108 109 sysctr_irq_acknowledge(evt);110 111 evt->event_handler(evt);112 113 return IRQ_HANDLED;114}115 116static struct timer_of to_sysctr = {117 .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,118 .clkevt = {119 .name = "i.MX system counter timer",120 .features = CLOCK_EVT_FEAT_ONESHOT |121 CLOCK_EVT_FEAT_DYNIRQ,122 .set_state_oneshot = sysctr_set_state_oneshot,123 .set_next_event = sysctr_set_next_event,124 .set_state_shutdown = sysctr_set_state_shutdown,125 .rating = 200,126 },127 .of_irq = {128 .handler = sysctr_timer_interrupt,129 .flags = IRQF_TIMER,130 },131 .of_clk = {132 .name = "per",133 },134};135 136static int __init __sysctr_timer_init(struct device_node *np)137{138 struct sysctr_private *priv;139 void __iomem *base;140 int ret;141 142 priv = kzalloc(sizeof(struct sysctr_private), GFP_KERNEL);143 if (!priv)144 return -ENOMEM;145 146 ret = timer_of_init(np, &to_sysctr);147 if (ret) {148 kfree(priv);149 return ret;150 }151 152 if (!of_property_read_bool(np, "nxp,no-divider")) {153 /* system counter clock is divided by 3 internally */154 to_sysctr.of_clk.rate /= SYS_CTR_CLK_DIV;155 }156 157 to_sysctr.clkevt.cpumask = cpu_possible_mask;158 to_sysctr.private_data = priv;159 160 base = timer_of_base(&to_sysctr);161 priv->cmpcr = readl(base + CMPCR) & ~SYS_CTR_EN;162 163 return 0;164}165 166static int __init sysctr_timer_init(struct device_node *np)167{168 struct sysctr_private *priv;169 int ret;170 171 ret = __sysctr_timer_init(np);172 if (ret)173 return ret;174 175 priv = to_sysctr.private_data;176 priv->lo_off = CNTCV_LO;177 priv->hi_off = CNTCV_HI;178 179 clockevents_config_and_register(&to_sysctr.clkevt,180 timer_of_rate(&to_sysctr),181 0xff, 0x7fffffff);182 183 return 0;184}185 186static int __init sysctr_timer_imx95_init(struct device_node *np)187{188 struct sysctr_private *priv;189 int ret;190 191 ret = __sysctr_timer_init(np);192 if (ret)193 return ret;194 195 priv = to_sysctr.private_data;196 priv->lo_off = CNTCV_LO_IMX95;197 priv->hi_off = CNTCV_HI_IMX95;198 199 clockevents_config_and_register(&to_sysctr.clkevt,200 timer_of_rate(&to_sysctr),201 0xff, 0x7fffffff);202 203 return 0;204}205 206TIMER_OF_DECLARE(sysctr_timer, "nxp,sysctr-timer", sysctr_timer_init);207TIMER_OF_DECLARE(sysctr_timer_imx95, "nxp,imx95-sysctr-timer", sysctr_timer_imx95_init);208