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-microchip-pit64b.c (13688B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * 64-bit Periodic Interval Timer driver
      4 *
      5 * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
      6 *
      7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
      8 */
      9
     10#include <linux/clk.h>
     11#include <linux/clockchips.h>
     12#include <linux/interrupt.h>
     13#include <linux/of_address.h>
     14#include <linux/of_irq.h>
     15#include <linux/sched_clock.h>
     16#include <linux/slab.h>
     17
     18#define MCHP_PIT64B_CR			0x00	/* Control Register */
     19#define MCHP_PIT64B_CR_START		BIT(0)
     20#define MCHP_PIT64B_CR_SWRST		BIT(8)
     21
     22#define MCHP_PIT64B_MR			0x04	/* Mode Register */
     23#define MCHP_PIT64B_MR_CONT		BIT(0)
     24#define MCHP_PIT64B_MR_ONE_SHOT		(0)
     25#define MCHP_PIT64B_MR_SGCLK		BIT(3)
     26#define MCHP_PIT64B_MR_PRES		GENMASK(11, 8)
     27
     28#define MCHP_PIT64B_LSB_PR		0x08	/* LSB Period Register */
     29
     30#define MCHP_PIT64B_MSB_PR		0x0C	/* MSB Period Register */
     31
     32#define MCHP_PIT64B_IER			0x10	/* Interrupt Enable Register */
     33#define MCHP_PIT64B_IER_PERIOD		BIT(0)
     34
     35#define MCHP_PIT64B_ISR			0x1C	/* Interrupt Status Register */
     36
     37#define MCHP_PIT64B_TLSBR		0x20	/* Timer LSB Register */
     38
     39#define MCHP_PIT64B_TMSBR		0x24	/* Timer MSB Register */
     40
     41#define MCHP_PIT64B_PRES_MAX		0x10
     42#define MCHP_PIT64B_LSBMASK		GENMASK_ULL(31, 0)
     43#define MCHP_PIT64B_PRES_TO_MODE(p)	(MCHP_PIT64B_MR_PRES & ((p) << 8))
     44#define MCHP_PIT64B_MODE_TO_PRES(m)	((MCHP_PIT64B_MR_PRES & (m)) >> 8)
     45#define MCHP_PIT64B_DEF_FREQ		5000000UL	/* 5 MHz */
     46
     47#define MCHP_PIT64B_NAME		"pit64b"
     48
     49/**
     50 * struct mchp_pit64b_timer - PIT64B timer data structure
     51 * @base: base address of PIT64B hardware block
     52 * @pclk: PIT64B's peripheral clock
     53 * @gclk: PIT64B's generic clock
     54 * @mode: precomputed value for mode register
     55 */
     56struct mchp_pit64b_timer {
     57	void __iomem	*base;
     58	struct clk	*pclk;
     59	struct clk	*gclk;
     60	u32		mode;
     61};
     62
     63/**
     64 * mchp_pit64b_clkevt - PIT64B clockevent data structure
     65 * @timer: PIT64B timer
     66 * @clkevt: clockevent
     67 */
     68struct mchp_pit64b_clkevt {
     69	struct mchp_pit64b_timer	timer;
     70	struct clock_event_device	clkevt;
     71};
     72
     73#define clkevt_to_mchp_pit64b_timer(x) \
     74	((struct mchp_pit64b_timer *)container_of(x,\
     75		struct mchp_pit64b_clkevt, clkevt))
     76
     77/**
     78 * mchp_pit64b_clksrc - PIT64B clocksource data structure
     79 * @timer: PIT64B timer
     80 * @clksrc: clocksource
     81 */
     82struct mchp_pit64b_clksrc {
     83	struct mchp_pit64b_timer	timer;
     84	struct clocksource		clksrc;
     85};
     86
     87#define clksrc_to_mchp_pit64b_timer(x) \
     88	((struct mchp_pit64b_timer *)container_of(x,\
     89		struct mchp_pit64b_clksrc, clksrc))
     90
     91/* Base address for clocksource timer. */
     92static void __iomem *mchp_pit64b_cs_base;
     93/* Default cycles for clockevent timer. */
     94static u64 mchp_pit64b_ce_cycles;
     95
     96static inline u64 mchp_pit64b_cnt_read(void __iomem *base)
     97{
     98	unsigned long	flags;
     99	u32		low, high;
    100
    101	raw_local_irq_save(flags);
    102
    103	/*
    104	 * When using a 64 bit period TLSB must be read first, followed by the
    105	 * read of TMSB. This sequence generates an atomic read of the 64 bit
    106	 * timer value whatever the lapse of time between the accesses.
    107	 */
    108	low = readl_relaxed(base + MCHP_PIT64B_TLSBR);
    109	high = readl_relaxed(base + MCHP_PIT64B_TMSBR);
    110
    111	raw_local_irq_restore(flags);
    112
    113	return (((u64)high << 32) | low);
    114}
    115
    116static inline void mchp_pit64b_reset(struct mchp_pit64b_timer *timer,
    117				     u64 cycles, u32 mode, u32 irqs)
    118{
    119	u32 low, high;
    120
    121	low = cycles & MCHP_PIT64B_LSBMASK;
    122	high = cycles >> 32;
    123
    124	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
    125	writel_relaxed(mode | timer->mode, timer->base + MCHP_PIT64B_MR);
    126	writel_relaxed(high, timer->base + MCHP_PIT64B_MSB_PR);
    127	writel_relaxed(low, timer->base + MCHP_PIT64B_LSB_PR);
    128	writel_relaxed(irqs, timer->base + MCHP_PIT64B_IER);
    129	writel_relaxed(MCHP_PIT64B_CR_START, timer->base + MCHP_PIT64B_CR);
    130}
    131
    132static void mchp_pit64b_suspend(struct mchp_pit64b_timer *timer)
    133{
    134	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
    135	if (timer->mode & MCHP_PIT64B_MR_SGCLK)
    136		clk_disable_unprepare(timer->gclk);
    137	clk_disable_unprepare(timer->pclk);
    138}
    139
    140static void mchp_pit64b_resume(struct mchp_pit64b_timer *timer)
    141{
    142	clk_prepare_enable(timer->pclk);
    143	if (timer->mode & MCHP_PIT64B_MR_SGCLK)
    144		clk_prepare_enable(timer->gclk);
    145}
    146
    147static void mchp_pit64b_clksrc_suspend(struct clocksource *cs)
    148{
    149	struct mchp_pit64b_timer *timer = clksrc_to_mchp_pit64b_timer(cs);
    150
    151	mchp_pit64b_suspend(timer);
    152}
    153
    154static void mchp_pit64b_clksrc_resume(struct clocksource *cs)
    155{
    156	struct mchp_pit64b_timer *timer = clksrc_to_mchp_pit64b_timer(cs);
    157
    158	mchp_pit64b_resume(timer);
    159	mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
    160}
    161
    162static u64 mchp_pit64b_clksrc_read(struct clocksource *cs)
    163{
    164	return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
    165}
    166
    167static u64 notrace mchp_pit64b_sched_read_clk(void)
    168{
    169	return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
    170}
    171
    172static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
    173{
    174	struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
    175
    176	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
    177
    178	return 0;
    179}
    180
    181static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
    182{
    183	struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
    184
    185	mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT,
    186			  MCHP_PIT64B_IER_PERIOD);
    187
    188	return 0;
    189}
    190
    191static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
    192					     struct clock_event_device *cedev)
    193{
    194	struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
    195
    196	mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT,
    197			  MCHP_PIT64B_IER_PERIOD);
    198
    199	return 0;
    200}
    201
    202static void mchp_pit64b_clkevt_suspend(struct clock_event_device *cedev)
    203{
    204	struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
    205
    206	mchp_pit64b_suspend(timer);
    207}
    208
    209static void mchp_pit64b_clkevt_resume(struct clock_event_device *cedev)
    210{
    211	struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
    212
    213	mchp_pit64b_resume(timer);
    214}
    215
    216static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id)
    217{
    218	struct mchp_pit64b_clkevt *irq_data = dev_id;
    219
    220	/* Need to clear the interrupt. */
    221	readl_relaxed(irq_data->timer.base + MCHP_PIT64B_ISR);
    222
    223	irq_data->clkevt.event_handler(&irq_data->clkevt);
    224
    225	return IRQ_HANDLED;
    226}
    227
    228static void __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate,
    229					    u32 max_rate)
    230{
    231	u32 tmp;
    232
    233	for (*pres = 0; *pres < MCHP_PIT64B_PRES_MAX; (*pres)++) {
    234		tmp = clk_rate / (*pres + 1);
    235		if (tmp <= max_rate)
    236			break;
    237	}
    238
    239	/* Use the biggest prescaler if we didn't match one. */
    240	if (*pres == MCHP_PIT64B_PRES_MAX)
    241		*pres = MCHP_PIT64B_PRES_MAX - 1;
    242}
    243
    244/**
    245 * mchp_pit64b_init_mode - prepare PIT64B mode register value to be used at
    246 *			   runtime; this includes prescaler and SGCLK bit
    247 *
    248 * PIT64B timer may be fed by gclk or pclk. When gclk is used its rate has to
    249 * be at least 3 times lower that pclk's rate. pclk rate is fixed, gclk rate
    250 * could be changed via clock APIs. The chosen clock (pclk or gclk) could be
    251 * divided by the internal PIT64B's divider.
    252 *
    253 * This function, first tries to use GCLK by requesting the desired rate from
    254 * PMC and then using the internal PIT64B prescaler, if any, to reach the
    255 * requested rate. If PCLK/GCLK < 3 (condition requested by PIT64B hardware)
    256 * then the function falls back on using PCLK as clock source for PIT64B timer
    257 * choosing the highest prescaler in case it doesn't locate one to match the
    258 * requested frequency.
    259 *
    260 * Below is presented the PIT64B block in relation with PMC:
    261 *
    262 *                                PIT64B
    263 *  PMC             +------------------------------------+
    264 * +----+           |   +-----+                          |
    265 * |    |-->gclk -->|-->|     |    +---------+  +-----+  |
    266 * |    |           |   | MUX |--->| Divider |->|timer|  |
    267 * |    |-->pclk -->|-->|     |    +---------+  +-----+  |
    268 * +----+           |   +-----+                          |
    269 *                  |      ^                             |
    270 *                  |     sel                            |
    271 *                  +------------------------------------+
    272 *
    273 * Where:
    274 *	- gclk rate <= pclk rate/3
    275 *	- gclk rate could be requested from PMC
    276 *	- pclk rate is fixed (cannot be requested from PMC)
    277 */
    278static int __init mchp_pit64b_init_mode(struct mchp_pit64b_timer *timer,
    279					unsigned long max_rate)
    280{
    281	unsigned long pclk_rate, diff = 0, best_diff = ULONG_MAX;
    282	long gclk_round = 0;
    283	u32 pres, best_pres = 0;
    284
    285	pclk_rate = clk_get_rate(timer->pclk);
    286	if (!pclk_rate)
    287		return -EINVAL;
    288
    289	timer->mode = 0;
    290
    291	/* Try using GCLK. */
    292	gclk_round = clk_round_rate(timer->gclk, max_rate);
    293	if (gclk_round < 0)
    294		goto pclk;
    295
    296	if (pclk_rate / gclk_round < 3)
    297		goto pclk;
    298
    299	mchp_pit64b_pres_compute(&pres, gclk_round, max_rate);
    300	best_diff = abs(gclk_round / (pres + 1) - max_rate);
    301	best_pres = pres;
    302
    303	if (!best_diff) {
    304		timer->mode |= MCHP_PIT64B_MR_SGCLK;
    305		clk_set_rate(timer->gclk, gclk_round);
    306		goto done;
    307	}
    308
    309pclk:
    310	/* Check if requested rate could be obtained using PCLK. */
    311	mchp_pit64b_pres_compute(&pres, pclk_rate, max_rate);
    312	diff = abs(pclk_rate / (pres + 1) - max_rate);
    313
    314	if (best_diff > diff) {
    315		/* Use PCLK. */
    316		best_pres = pres;
    317	} else {
    318		/* Use GCLK. */
    319		timer->mode |= MCHP_PIT64B_MR_SGCLK;
    320		clk_set_rate(timer->gclk, gclk_round);
    321	}
    322
    323done:
    324	timer->mode |= MCHP_PIT64B_PRES_TO_MODE(best_pres);
    325
    326	pr_info("PIT64B: using clk=%s with prescaler %u, freq=%lu [Hz]\n",
    327		timer->mode & MCHP_PIT64B_MR_SGCLK ? "gclk" : "pclk", best_pres,
    328		timer->mode & MCHP_PIT64B_MR_SGCLK ?
    329		gclk_round / (best_pres + 1) : pclk_rate / (best_pres + 1));
    330
    331	return 0;
    332}
    333
    334static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer,
    335					  u32 clk_rate)
    336{
    337	struct mchp_pit64b_clksrc *cs;
    338	int ret;
    339
    340	cs = kzalloc(sizeof(*cs), GFP_KERNEL);
    341	if (!cs)
    342		return -ENOMEM;
    343
    344	mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
    345
    346	mchp_pit64b_cs_base = timer->base;
    347
    348	cs->timer.base = timer->base;
    349	cs->timer.pclk = timer->pclk;
    350	cs->timer.gclk = timer->gclk;
    351	cs->timer.mode = timer->mode;
    352	cs->clksrc.name = MCHP_PIT64B_NAME;
    353	cs->clksrc.mask = CLOCKSOURCE_MASK(64);
    354	cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
    355	cs->clksrc.rating = 210;
    356	cs->clksrc.read = mchp_pit64b_clksrc_read;
    357	cs->clksrc.suspend = mchp_pit64b_clksrc_suspend;
    358	cs->clksrc.resume = mchp_pit64b_clksrc_resume;
    359
    360	ret = clocksource_register_hz(&cs->clksrc, clk_rate);
    361	if (ret) {
    362		pr_debug("clksrc: Failed to register PIT64B clocksource!\n");
    363
    364		/* Stop timer. */
    365		writel_relaxed(MCHP_PIT64B_CR_SWRST,
    366			       timer->base + MCHP_PIT64B_CR);
    367		kfree(cs);
    368
    369		return ret;
    370	}
    371
    372	sched_clock_register(mchp_pit64b_sched_read_clk, 64, clk_rate);
    373
    374	return 0;
    375}
    376
    377static int __init mchp_pit64b_init_clkevt(struct mchp_pit64b_timer *timer,
    378					  u32 clk_rate, u32 irq)
    379{
    380	struct mchp_pit64b_clkevt *ce;
    381	int ret;
    382
    383	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
    384	if (!ce)
    385		return -ENOMEM;
    386
    387	mchp_pit64b_ce_cycles = DIV_ROUND_CLOSEST(clk_rate, HZ);
    388
    389	ce->timer.base = timer->base;
    390	ce->timer.pclk = timer->pclk;
    391	ce->timer.gclk = timer->gclk;
    392	ce->timer.mode = timer->mode;
    393	ce->clkevt.name = MCHP_PIT64B_NAME;
    394	ce->clkevt.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC;
    395	ce->clkevt.rating = 150;
    396	ce->clkevt.set_state_shutdown = mchp_pit64b_clkevt_shutdown;
    397	ce->clkevt.set_state_periodic = mchp_pit64b_clkevt_set_periodic;
    398	ce->clkevt.set_next_event = mchp_pit64b_clkevt_set_next_event;
    399	ce->clkevt.suspend = mchp_pit64b_clkevt_suspend;
    400	ce->clkevt.resume = mchp_pit64b_clkevt_resume;
    401	ce->clkevt.cpumask = cpumask_of(0);
    402	ce->clkevt.irq = irq;
    403
    404	ret = request_irq(irq, mchp_pit64b_interrupt, IRQF_TIMER,
    405			  "pit64b_tick", ce);
    406	if (ret) {
    407		pr_debug("clkevt: Failed to setup PIT64B IRQ\n");
    408		kfree(ce);
    409		return ret;
    410	}
    411
    412	clockevents_config_and_register(&ce->clkevt, clk_rate, 1, ULONG_MAX);
    413
    414	return 0;
    415}
    416
    417static int __init mchp_pit64b_dt_init_timer(struct device_node *node,
    418					    bool clkevt)
    419{
    420	struct mchp_pit64b_timer timer;
    421	unsigned long clk_rate;
    422	u32 irq = 0;
    423	int ret;
    424
    425	/* Parse DT node. */
    426	timer.pclk = of_clk_get_by_name(node, "pclk");
    427	if (IS_ERR(timer.pclk))
    428		return PTR_ERR(timer.pclk);
    429
    430	timer.gclk = of_clk_get_by_name(node, "gclk");
    431	if (IS_ERR(timer.gclk))
    432		return PTR_ERR(timer.gclk);
    433
    434	timer.base = of_iomap(node, 0);
    435	if (!timer.base)
    436		return -ENXIO;
    437
    438	if (clkevt) {
    439		irq = irq_of_parse_and_map(node, 0);
    440		if (!irq) {
    441			ret = -ENODEV;
    442			goto io_unmap;
    443		}
    444	}
    445
    446	/* Initialize mode (prescaler + SGCK bit). To be used at runtime. */
    447	ret = mchp_pit64b_init_mode(&timer, MCHP_PIT64B_DEF_FREQ);
    448	if (ret)
    449		goto irq_unmap;
    450
    451	ret = clk_prepare_enable(timer.pclk);
    452	if (ret)
    453		goto irq_unmap;
    454
    455	if (timer.mode & MCHP_PIT64B_MR_SGCLK) {
    456		ret = clk_prepare_enable(timer.gclk);
    457		if (ret)
    458			goto pclk_unprepare;
    459
    460		clk_rate = clk_get_rate(timer.gclk);
    461	} else {
    462		clk_rate = clk_get_rate(timer.pclk);
    463	}
    464	clk_rate = clk_rate / (MCHP_PIT64B_MODE_TO_PRES(timer.mode) + 1);
    465
    466	if (clkevt)
    467		ret = mchp_pit64b_init_clkevt(&timer, clk_rate, irq);
    468	else
    469		ret = mchp_pit64b_init_clksrc(&timer, clk_rate);
    470
    471	if (ret)
    472		goto gclk_unprepare;
    473
    474	return 0;
    475
    476gclk_unprepare:
    477	if (timer.mode & MCHP_PIT64B_MR_SGCLK)
    478		clk_disable_unprepare(timer.gclk);
    479pclk_unprepare:
    480	clk_disable_unprepare(timer.pclk);
    481irq_unmap:
    482	irq_dispose_mapping(irq);
    483io_unmap:
    484	iounmap(timer.base);
    485
    486	return ret;
    487}
    488
    489static int __init mchp_pit64b_dt_init(struct device_node *node)
    490{
    491	static int inits;
    492
    493	switch (inits++) {
    494	case 0:
    495		/* 1st request, register clockevent. */
    496		return mchp_pit64b_dt_init_timer(node, true);
    497	case 1:
    498		/* 2nd request, register clocksource. */
    499		return mchp_pit64b_dt_init_timer(node, false);
    500	}
    501
    502	/* The rest, don't care. */
    503	return -EINVAL;
    504}
    505
    506TIMER_OF_DECLARE(mchp_pit64b, "microchip,sam9x60-pit64b", mchp_pit64b_dt_init);