brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · 4452581 Raw
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