cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

timer-fsl-ftm.c (7914B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Freescale FlexTimer Module (FTM) timer driver.
      4 *
      5 * Copyright 2014 Freescale Semiconductor, Inc.
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/clockchips.h>
     10#include <linux/clocksource.h>
     11#include <linux/err.h>
     12#include <linux/interrupt.h>
     13#include <linux/io.h>
     14#include <linux/of_address.h>
     15#include <linux/of_irq.h>
     16#include <linux/sched_clock.h>
     17#include <linux/slab.h>
     18#include <linux/fsl/ftm.h>
     19
     20#define FTM_SC_CLK(c)	((c) << FTM_SC_CLK_MASK_SHIFT)
     21
     22struct ftm_clock_device {
     23	void __iomem *clksrc_base;
     24	void __iomem *clkevt_base;
     25	unsigned long periodic_cyc;
     26	unsigned long ps;
     27	bool big_endian;
     28};
     29
     30static struct ftm_clock_device *priv;
     31
     32static inline u32 ftm_readl(void __iomem *addr)
     33{
     34	if (priv->big_endian)
     35		return ioread32be(addr);
     36	else
     37		return ioread32(addr);
     38}
     39
     40static inline void ftm_writel(u32 val, void __iomem *addr)
     41{
     42	if (priv->big_endian)
     43		iowrite32be(val, addr);
     44	else
     45		iowrite32(val, addr);
     46}
     47
     48static inline void ftm_counter_enable(void __iomem *base)
     49{
     50	u32 val;
     51
     52	/* select and enable counter clock source */
     53	val = ftm_readl(base + FTM_SC);
     54	val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
     55	val |= priv->ps | FTM_SC_CLK(1);
     56	ftm_writel(val, base + FTM_SC);
     57}
     58
     59static inline void ftm_counter_disable(void __iomem *base)
     60{
     61	u32 val;
     62
     63	/* disable counter clock source */
     64	val = ftm_readl(base + FTM_SC);
     65	val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
     66	ftm_writel(val, base + FTM_SC);
     67}
     68
     69static inline void ftm_irq_acknowledge(void __iomem *base)
     70{
     71	u32 val;
     72
     73	val = ftm_readl(base + FTM_SC);
     74	val &= ~FTM_SC_TOF;
     75	ftm_writel(val, base + FTM_SC);
     76}
     77
     78static inline void ftm_irq_enable(void __iomem *base)
     79{
     80	u32 val;
     81
     82	val = ftm_readl(base + FTM_SC);
     83	val |= FTM_SC_TOIE;
     84	ftm_writel(val, base + FTM_SC);
     85}
     86
     87static inline void ftm_irq_disable(void __iomem *base)
     88{
     89	u32 val;
     90
     91	val = ftm_readl(base + FTM_SC);
     92	val &= ~FTM_SC_TOIE;
     93	ftm_writel(val, base + FTM_SC);
     94}
     95
     96static inline void ftm_reset_counter(void __iomem *base)
     97{
     98	/*
     99	 * The CNT register contains the FTM counter value.
    100	 * Reset clears the CNT register. Writing any value to COUNT
    101	 * updates the counter with its initial value, CNTIN.
    102	 */
    103	ftm_writel(0x00, base + FTM_CNT);
    104}
    105
    106static u64 notrace ftm_read_sched_clock(void)
    107{
    108	return ftm_readl(priv->clksrc_base + FTM_CNT);
    109}
    110
    111static int ftm_set_next_event(unsigned long delta,
    112				struct clock_event_device *unused)
    113{
    114	/*
    115	 * The CNNIN and MOD are all double buffer registers, writing
    116	 * to the MOD register latches the value into a buffer. The MOD
    117	 * register is updated with the value of its write buffer with
    118	 * the following scenario:
    119	 * a, the counter source clock is disabled.
    120	 */
    121	ftm_counter_disable(priv->clkevt_base);
    122
    123	/* Force the value of CNTIN to be loaded into the FTM counter */
    124	ftm_reset_counter(priv->clkevt_base);
    125
    126	/*
    127	 * The counter increments until the value of MOD is reached,
    128	 * at which point the counter is reloaded with the value of CNTIN.
    129	 * The TOF (the overflow flag) bit is set when the FTM counter
    130	 * changes from MOD to CNTIN. So we should using the delta - 1.
    131	 */
    132	ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD);
    133
    134	ftm_counter_enable(priv->clkevt_base);
    135
    136	ftm_irq_enable(priv->clkevt_base);
    137
    138	return 0;
    139}
    140
    141static int ftm_set_oneshot(struct clock_event_device *evt)
    142{
    143	ftm_counter_disable(priv->clkevt_base);
    144	return 0;
    145}
    146
    147static int ftm_set_periodic(struct clock_event_device *evt)
    148{
    149	ftm_set_next_event(priv->periodic_cyc, evt);
    150	return 0;
    151}
    152
    153static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id)
    154{
    155	struct clock_event_device *evt = dev_id;
    156
    157	ftm_irq_acknowledge(priv->clkevt_base);
    158
    159	if (likely(clockevent_state_oneshot(evt))) {
    160		ftm_irq_disable(priv->clkevt_base);
    161		ftm_counter_disable(priv->clkevt_base);
    162	}
    163
    164	evt->event_handler(evt);
    165
    166	return IRQ_HANDLED;
    167}
    168
    169static struct clock_event_device ftm_clockevent = {
    170	.name			= "Freescale ftm timer",
    171	.features		= CLOCK_EVT_FEAT_PERIODIC |
    172				  CLOCK_EVT_FEAT_ONESHOT,
    173	.set_state_periodic	= ftm_set_periodic,
    174	.set_state_oneshot	= ftm_set_oneshot,
    175	.set_next_event		= ftm_set_next_event,
    176	.rating			= 300,
    177};
    178
    179static int __init ftm_clockevent_init(unsigned long freq, int irq)
    180{
    181	int err;
    182
    183	ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
    184	ftm_writel(~0u, priv->clkevt_base + FTM_MOD);
    185
    186	ftm_reset_counter(priv->clkevt_base);
    187
    188	err = request_irq(irq, ftm_evt_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
    189			  "Freescale ftm timer", &ftm_clockevent);
    190	if (err) {
    191		pr_err("ftm: setup irq failed: %d\n", err);
    192		return err;
    193	}
    194
    195	ftm_clockevent.cpumask = cpumask_of(0);
    196	ftm_clockevent.irq = irq;
    197
    198	clockevents_config_and_register(&ftm_clockevent,
    199					freq / (1 << priv->ps),
    200					1, 0xffff);
    201
    202	ftm_counter_enable(priv->clkevt_base);
    203
    204	return 0;
    205}
    206
    207static int __init ftm_clocksource_init(unsigned long freq)
    208{
    209	int err;
    210
    211	ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
    212	ftm_writel(~0u, priv->clksrc_base + FTM_MOD);
    213
    214	ftm_reset_counter(priv->clksrc_base);
    215
    216	sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
    217	err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
    218				    freq / (1 << priv->ps), 300, 16,
    219				    clocksource_mmio_readl_up);
    220	if (err) {
    221		pr_err("ftm: init clock source mmio failed: %d\n", err);
    222		return err;
    223	}
    224
    225	ftm_counter_enable(priv->clksrc_base);
    226
    227	return 0;
    228}
    229
    230static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
    231				 char *ftm_name)
    232{
    233	struct clk *clk;
    234	int err;
    235
    236	clk = of_clk_get_by_name(np, cnt_name);
    237	if (IS_ERR(clk)) {
    238		pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
    239		return PTR_ERR(clk);
    240	}
    241	err = clk_prepare_enable(clk);
    242	if (err) {
    243		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
    244			cnt_name, err);
    245		return err;
    246	}
    247
    248	clk = of_clk_get_by_name(np, ftm_name);
    249	if (IS_ERR(clk)) {
    250		pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
    251		return PTR_ERR(clk);
    252	}
    253	err = clk_prepare_enable(clk);
    254	if (err)
    255		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
    256			ftm_name, err);
    257
    258	return clk_get_rate(clk);
    259}
    260
    261static unsigned long __init ftm_clk_init(struct device_node *np)
    262{
    263	long freq;
    264
    265	freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
    266	if (freq <= 0)
    267		return 0;
    268
    269	freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
    270	if (freq <= 0)
    271		return 0;
    272
    273	return freq;
    274}
    275
    276static int __init ftm_calc_closest_round_cyc(unsigned long freq)
    277{
    278	priv->ps = 0;
    279
    280	/* The counter register is only using the lower 16 bits, and
    281	 * if the 'freq' value is to big here, then the periodic_cyc
    282	 * may exceed 0xFFFF.
    283	 */
    284	do {
    285		priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
    286						HZ * (1 << priv->ps++));
    287	} while (priv->periodic_cyc > 0xFFFF);
    288
    289	if (priv->ps > FTM_PS_MAX) {
    290		pr_err("ftm: the prescaler is %lu > %d\n",
    291				priv->ps, FTM_PS_MAX);
    292		return -EINVAL;
    293	}
    294
    295	return 0;
    296}
    297
    298static int __init ftm_timer_init(struct device_node *np)
    299{
    300	unsigned long freq;
    301	int ret, irq;
    302
    303	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    304	if (!priv)
    305		return -ENOMEM;
    306
    307	ret = -ENXIO;
    308	priv->clkevt_base = of_iomap(np, 0);
    309	if (!priv->clkevt_base) {
    310		pr_err("ftm: unable to map event timer registers\n");
    311		goto err_clkevt;
    312	}
    313
    314	priv->clksrc_base = of_iomap(np, 1);
    315	if (!priv->clksrc_base) {
    316		pr_err("ftm: unable to map source timer registers\n");
    317		goto err_clksrc;
    318	}
    319
    320	ret = -EINVAL;
    321	irq = irq_of_parse_and_map(np, 0);
    322	if (irq <= 0) {
    323		pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
    324		goto err;
    325	}
    326
    327	priv->big_endian = of_property_read_bool(np, "big-endian");
    328
    329	freq = ftm_clk_init(np);
    330	if (!freq)
    331		goto err;
    332
    333	ret = ftm_calc_closest_round_cyc(freq);
    334	if (ret)
    335		goto err;
    336
    337	ret = ftm_clocksource_init(freq);
    338	if (ret)
    339		goto err;
    340
    341	ret = ftm_clockevent_init(freq, irq);
    342	if (ret)
    343		goto err;
    344
    345	return 0;
    346
    347err:
    348	iounmap(priv->clksrc_base);
    349err_clksrc:
    350	iounmap(priv->clkevt_base);
    351err_clkevt:
    352	kfree(priv);
    353	return ret;
    354}
    355TIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);