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

cttimer.c (11165B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * PCM timer handling on ctxfi
      4 */
      5
      6#include <linux/slab.h>
      7#include <linux/math64.h>
      8#include <linux/moduleparam.h>
      9#include <sound/core.h>
     10#include <sound/pcm.h>
     11#include "ctatc.h"
     12#include "cthardware.h"
     13#include "cttimer.h"
     14
     15static bool use_system_timer;
     16MODULE_PARM_DESC(use_system_timer, "Force to use system-timer");
     17module_param(use_system_timer, bool, 0444);
     18
     19struct ct_timer_ops {
     20	void (*init)(struct ct_timer_instance *);
     21	void (*prepare)(struct ct_timer_instance *);
     22	void (*start)(struct ct_timer_instance *);
     23	void (*stop)(struct ct_timer_instance *);
     24	void (*free_instance)(struct ct_timer_instance *);
     25	void (*interrupt)(struct ct_timer *);
     26	void (*free_global)(struct ct_timer *);
     27};
     28
     29/* timer instance -- assigned to each PCM stream */
     30struct ct_timer_instance {
     31	spinlock_t lock;
     32	struct ct_timer *timer_base;
     33	struct ct_atc_pcm *apcm;
     34	struct snd_pcm_substream *substream;
     35	struct timer_list timer;
     36	struct list_head instance_list;
     37	struct list_head running_list;
     38	unsigned int position;
     39	unsigned int frag_count;
     40	unsigned int running:1;
     41	unsigned int need_update:1;
     42};
     43
     44/* timer instance manager */
     45struct ct_timer {
     46	spinlock_t lock;		/* global timer lock (for xfitimer) */
     47	spinlock_t list_lock;		/* lock for instance list */
     48	struct ct_atc *atc;
     49	const struct ct_timer_ops *ops;
     50	struct list_head instance_head;
     51	struct list_head running_head;
     52	unsigned int wc;		/* current wallclock */
     53	unsigned int irq_handling:1;	/* in IRQ handling */
     54	unsigned int reprogram:1;	/* need to reprogram the internval */
     55	unsigned int running:1;		/* global timer running */
     56};
     57
     58
     59/*
     60 * system-timer-based updates
     61 */
     62
     63static void ct_systimer_callback(struct timer_list *t)
     64{
     65	struct ct_timer_instance *ti = from_timer(ti, t, timer);
     66	struct snd_pcm_substream *substream = ti->substream;
     67	struct snd_pcm_runtime *runtime = substream->runtime;
     68	struct ct_atc_pcm *apcm = ti->apcm;
     69	unsigned int period_size = runtime->period_size;
     70	unsigned int buffer_size = runtime->buffer_size;
     71	unsigned long flags;
     72	unsigned int position, dist, interval;
     73
     74	position = substream->ops->pointer(substream);
     75	dist = (position + buffer_size - ti->position) % buffer_size;
     76	if (dist >= period_size ||
     77	    position / period_size != ti->position / period_size) {
     78		apcm->interrupt(apcm);
     79		ti->position = position;
     80	}
     81	/* Add extra HZ*5/1000 to avoid overrun issue when recording
     82	 * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
     83	interval = ((period_size - (position % period_size))
     84		   * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
     85	spin_lock_irqsave(&ti->lock, flags);
     86	if (ti->running)
     87		mod_timer(&ti->timer, jiffies + interval);
     88	spin_unlock_irqrestore(&ti->lock, flags);
     89}
     90
     91static void ct_systimer_init(struct ct_timer_instance *ti)
     92{
     93	timer_setup(&ti->timer, ct_systimer_callback, 0);
     94}
     95
     96static void ct_systimer_start(struct ct_timer_instance *ti)
     97{
     98	struct snd_pcm_runtime *runtime = ti->substream->runtime;
     99	unsigned long flags;
    100
    101	spin_lock_irqsave(&ti->lock, flags);
    102	ti->running = 1;
    103	mod_timer(&ti->timer,
    104		  jiffies + (runtime->period_size * HZ +
    105			     (runtime->rate - 1)) / runtime->rate);
    106	spin_unlock_irqrestore(&ti->lock, flags);
    107}
    108
    109static void ct_systimer_stop(struct ct_timer_instance *ti)
    110{
    111	unsigned long flags;
    112
    113	spin_lock_irqsave(&ti->lock, flags);
    114	ti->running = 0;
    115	del_timer(&ti->timer);
    116	spin_unlock_irqrestore(&ti->lock, flags);
    117}
    118
    119static void ct_systimer_prepare(struct ct_timer_instance *ti)
    120{
    121	ct_systimer_stop(ti);
    122	try_to_del_timer_sync(&ti->timer);
    123}
    124
    125#define ct_systimer_free	ct_systimer_prepare
    126
    127static const struct ct_timer_ops ct_systimer_ops = {
    128	.init = ct_systimer_init,
    129	.free_instance = ct_systimer_free,
    130	.prepare = ct_systimer_prepare,
    131	.start = ct_systimer_start,
    132	.stop = ct_systimer_stop,
    133};
    134
    135
    136/*
    137 * Handling multiple streams using a global emu20k1 timer irq
    138 */
    139
    140#define CT_TIMER_FREQ	48000
    141#define MIN_TICKS	1
    142#define MAX_TICKS	((1 << 13) - 1)
    143
    144static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks)
    145{
    146	struct hw *hw = atimer->atc->hw;
    147	if (ticks > MAX_TICKS)
    148		ticks = MAX_TICKS;
    149	hw->set_timer_tick(hw, ticks);
    150	if (!atimer->running)
    151		hw->set_timer_irq(hw, 1);
    152	atimer->running = 1;
    153}
    154
    155static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
    156{
    157	if (atimer->running) {
    158		struct hw *hw = atimer->atc->hw;
    159		hw->set_timer_irq(hw, 0);
    160		hw->set_timer_tick(hw, 0);
    161		atimer->running = 0;
    162	}
    163}
    164
    165static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer)
    166{
    167	struct hw *hw = atimer->atc->hw;
    168	return hw->get_wc(hw);
    169}
    170
    171/*
    172 * reprogram the timer interval;
    173 * checks the running instance list and determines the next timer interval.
    174 * also updates the each stream position, returns the number of streams
    175 * to call snd_pcm_period_elapsed() appropriately
    176 *
    177 * call this inside the lock and irq disabled
    178 */
    179static int ct_xfitimer_reprogram(struct ct_timer *atimer, int can_update)
    180{
    181	struct ct_timer_instance *ti;
    182	unsigned int min_intr = (unsigned int)-1;
    183	int updates = 0;
    184	unsigned int wc, diff;
    185
    186	if (list_empty(&atimer->running_head)) {
    187		ct_xfitimer_irq_stop(atimer);
    188		atimer->reprogram = 0; /* clear flag */
    189		return 0;
    190	}
    191
    192	wc = ct_xfitimer_get_wc(atimer);
    193	diff = wc - atimer->wc;
    194	atimer->wc = wc;
    195	list_for_each_entry(ti, &atimer->running_head, running_list) {
    196		if (ti->frag_count > diff)
    197			ti->frag_count -= diff;
    198		else {
    199			unsigned int pos;
    200			unsigned int period_size, rate;
    201
    202			period_size = ti->substream->runtime->period_size;
    203			rate = ti->substream->runtime->rate;
    204			pos = ti->substream->ops->pointer(ti->substream);
    205			if (pos / period_size != ti->position / period_size) {
    206				ti->need_update = 1;
    207				ti->position = pos;
    208				updates++;
    209			}
    210			pos %= period_size;
    211			pos = period_size - pos;
    212			ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ +
    213						 rate - 1, rate);
    214		}
    215		if (ti->need_update && !can_update)
    216			min_intr = 0; /* pending to the next irq */
    217		if (ti->frag_count < min_intr)
    218			min_intr = ti->frag_count;
    219	}
    220
    221	if (min_intr < MIN_TICKS)
    222		min_intr = MIN_TICKS;
    223	ct_xfitimer_irq_rearm(atimer, min_intr);
    224	atimer->reprogram = 0; /* clear flag */
    225	return updates;
    226}
    227
    228/* look through the instance list and call period_elapsed if needed */
    229static void ct_xfitimer_check_period(struct ct_timer *atimer)
    230{
    231	struct ct_timer_instance *ti;
    232	unsigned long flags;
    233
    234	spin_lock_irqsave(&atimer->list_lock, flags);
    235	list_for_each_entry(ti, &atimer->instance_head, instance_list) {
    236		if (ti->running && ti->need_update) {
    237			ti->need_update = 0;
    238			ti->apcm->interrupt(ti->apcm);
    239		}
    240	}
    241	spin_unlock_irqrestore(&atimer->list_lock, flags);
    242}
    243
    244/* Handle timer-interrupt */
    245static void ct_xfitimer_callback(struct ct_timer *atimer)
    246{
    247	int update;
    248	unsigned long flags;
    249
    250	spin_lock_irqsave(&atimer->lock, flags);
    251	atimer->irq_handling = 1;
    252	do {
    253		update = ct_xfitimer_reprogram(atimer, 1);
    254		spin_unlock(&atimer->lock);
    255		if (update)
    256			ct_xfitimer_check_period(atimer);
    257		spin_lock(&atimer->lock);
    258	} while (atimer->reprogram);
    259	atimer->irq_handling = 0;
    260	spin_unlock_irqrestore(&atimer->lock, flags);
    261}
    262
    263static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
    264{
    265	ti->frag_count = ti->substream->runtime->period_size;
    266	ti->running = 0;
    267	ti->need_update = 0;
    268}
    269
    270
    271/* start/stop the timer */
    272static void ct_xfitimer_update(struct ct_timer *atimer)
    273{
    274	unsigned long flags;
    275
    276	spin_lock_irqsave(&atimer->lock, flags);
    277	if (atimer->irq_handling) {
    278		/* reached from IRQ handler; let it handle later */
    279		atimer->reprogram = 1;
    280		spin_unlock_irqrestore(&atimer->lock, flags);
    281		return;
    282	}
    283
    284	ct_xfitimer_irq_stop(atimer);
    285	ct_xfitimer_reprogram(atimer, 0);
    286	spin_unlock_irqrestore(&atimer->lock, flags);
    287}
    288
    289static void ct_xfitimer_start(struct ct_timer_instance *ti)
    290{
    291	struct ct_timer *atimer = ti->timer_base;
    292	unsigned long flags;
    293
    294	spin_lock_irqsave(&atimer->lock, flags);
    295	if (list_empty(&ti->running_list))
    296		atimer->wc = ct_xfitimer_get_wc(atimer);
    297	ti->running = 1;
    298	ti->need_update = 0;
    299	list_add(&ti->running_list, &atimer->running_head);
    300	spin_unlock_irqrestore(&atimer->lock, flags);
    301	ct_xfitimer_update(atimer);
    302}
    303
    304static void ct_xfitimer_stop(struct ct_timer_instance *ti)
    305{
    306	struct ct_timer *atimer = ti->timer_base;
    307	unsigned long flags;
    308
    309	spin_lock_irqsave(&atimer->lock, flags);
    310	list_del_init(&ti->running_list);
    311	ti->running = 0;
    312	spin_unlock_irqrestore(&atimer->lock, flags);
    313	ct_xfitimer_update(atimer);
    314}
    315
    316static void ct_xfitimer_free_global(struct ct_timer *atimer)
    317{
    318	ct_xfitimer_irq_stop(atimer);
    319}
    320
    321static const struct ct_timer_ops ct_xfitimer_ops = {
    322	.prepare = ct_xfitimer_prepare,
    323	.start = ct_xfitimer_start,
    324	.stop = ct_xfitimer_stop,
    325	.interrupt = ct_xfitimer_callback,
    326	.free_global = ct_xfitimer_free_global,
    327};
    328
    329/*
    330 * timer instance
    331 */
    332
    333struct ct_timer_instance *
    334ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm)
    335{
    336	struct ct_timer_instance *ti;
    337
    338	ti = kzalloc(sizeof(*ti), GFP_KERNEL);
    339	if (!ti)
    340		return NULL;
    341	spin_lock_init(&ti->lock);
    342	INIT_LIST_HEAD(&ti->instance_list);
    343	INIT_LIST_HEAD(&ti->running_list);
    344	ti->timer_base = atimer;
    345	ti->apcm = apcm;
    346	ti->substream = apcm->substream;
    347	if (atimer->ops->init)
    348		atimer->ops->init(ti);
    349
    350	spin_lock_irq(&atimer->list_lock);
    351	list_add(&ti->instance_list, &atimer->instance_head);
    352	spin_unlock_irq(&atimer->list_lock);
    353
    354	return ti;
    355}
    356
    357void ct_timer_prepare(struct ct_timer_instance *ti)
    358{
    359	if (ti->timer_base->ops->prepare)
    360		ti->timer_base->ops->prepare(ti);
    361	ti->position = 0;
    362	ti->running = 0;
    363}
    364
    365void ct_timer_start(struct ct_timer_instance *ti)
    366{
    367	struct ct_timer *atimer = ti->timer_base;
    368	atimer->ops->start(ti);
    369}
    370
    371void ct_timer_stop(struct ct_timer_instance *ti)
    372{
    373	struct ct_timer *atimer = ti->timer_base;
    374	atimer->ops->stop(ti);
    375}
    376
    377void ct_timer_instance_free(struct ct_timer_instance *ti)
    378{
    379	struct ct_timer *atimer = ti->timer_base;
    380
    381	atimer->ops->stop(ti); /* to be sure */
    382	if (atimer->ops->free_instance)
    383		atimer->ops->free_instance(ti);
    384
    385	spin_lock_irq(&atimer->list_lock);
    386	list_del(&ti->instance_list);
    387	spin_unlock_irq(&atimer->list_lock);
    388
    389	kfree(ti);
    390}
    391
    392/*
    393 * timer manager
    394 */
    395
    396static void ct_timer_interrupt(void *data, unsigned int status)
    397{
    398	struct ct_timer *timer = data;
    399
    400	/* Interval timer interrupt */
    401	if ((status & IT_INT) && timer->ops->interrupt)
    402		timer->ops->interrupt(timer);
    403}
    404
    405struct ct_timer *ct_timer_new(struct ct_atc *atc)
    406{
    407	struct ct_timer *atimer;
    408	struct hw *hw;
    409
    410	atimer = kzalloc(sizeof(*atimer), GFP_KERNEL);
    411	if (!atimer)
    412		return NULL;
    413	spin_lock_init(&atimer->lock);
    414	spin_lock_init(&atimer->list_lock);
    415	INIT_LIST_HEAD(&atimer->instance_head);
    416	INIT_LIST_HEAD(&atimer->running_head);
    417	atimer->atc = atc;
    418	hw = atc->hw;
    419	if (!use_system_timer && hw->set_timer_irq) {
    420		dev_info(atc->card->dev, "Use xfi-native timer\n");
    421		atimer->ops = &ct_xfitimer_ops;
    422		hw->irq_callback_data = atimer;
    423		hw->irq_callback = ct_timer_interrupt;
    424	} else {
    425		dev_info(atc->card->dev, "Use system timer\n");
    426		atimer->ops = &ct_systimer_ops;
    427	}
    428	return atimer;
    429}
    430
    431void ct_timer_free(struct ct_timer *atimer)
    432{
    433	struct hw *hw = atimer->atc->hw;
    434	hw->irq_callback = NULL;
    435	if (atimer->ops->free_global)
    436		atimer->ops->free_global(atimer);
    437	kfree(atimer);
    438}
    439