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

imx-media-fim.c (11993B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Frame Interval Monitor.
      4 *
      5 * Copyright (c) 2016 Mentor Graphics Inc.
      6 */
      7#include <linux/delay.h>
      8#include <linux/irq.h>
      9#include <linux/module.h>
     10#include <linux/platform_device.h>
     11#include <linux/slab.h>
     12#include <linux/spinlock.h>
     13#include <media/v4l2-ctrls.h>
     14#include <media/v4l2-subdev.h>
     15#include <media/imx.h>
     16#include "imx-media.h"
     17
     18enum {
     19	FIM_CL_ENABLE = 0,
     20	FIM_CL_NUM,
     21	FIM_CL_TOLERANCE_MIN,
     22	FIM_CL_TOLERANCE_MAX,
     23	FIM_CL_NUM_SKIP,
     24	FIM_NUM_CONTROLS,
     25};
     26
     27enum {
     28	FIM_CL_ICAP_EDGE = 0,
     29	FIM_CL_ICAP_CHANNEL,
     30	FIM_NUM_ICAP_CONTROLS,
     31};
     32
     33#define FIM_CL_ENABLE_DEF          0 /* FIM disabled by default */
     34#define FIM_CL_NUM_DEF             8 /* average 8 frames */
     35#define FIM_CL_NUM_SKIP_DEF        2 /* skip 2 frames after restart */
     36#define FIM_CL_TOLERANCE_MIN_DEF  50 /* usec */
     37#define FIM_CL_TOLERANCE_MAX_DEF   0 /* no max tolerance (unbounded) */
     38
     39struct imx_media_fim {
     40	/* the owning subdev of this fim instance */
     41	struct v4l2_subdev *sd;
     42
     43	/* FIM's control handler */
     44	struct v4l2_ctrl_handler ctrl_handler;
     45
     46	/* control clusters */
     47	struct v4l2_ctrl  *ctrl[FIM_NUM_CONTROLS];
     48	struct v4l2_ctrl  *icap_ctrl[FIM_NUM_ICAP_CONTROLS];
     49
     50	spinlock_t        lock; /* protect control values */
     51
     52	/* current control values */
     53	bool              enabled;
     54	int               num_avg;
     55	int               num_skip;
     56	unsigned long     tolerance_min; /* usec */
     57	unsigned long     tolerance_max; /* usec */
     58	/* input capture method of measuring FI */
     59	int               icap_channel;
     60	int               icap_flags;
     61
     62	int               counter;
     63	ktime_t		  last_ts;
     64	unsigned long     sum;       /* usec */
     65	unsigned long     nominal;   /* usec */
     66
     67	struct completion icap_first_event;
     68	bool              stream_on;
     69};
     70
     71#define icap_enabled(fim) ((fim)->icap_flags != IRQ_TYPE_NONE)
     72
     73static void update_fim_nominal(struct imx_media_fim *fim,
     74			       const struct v4l2_fract *fi)
     75{
     76	if (fi->denominator == 0) {
     77		dev_dbg(fim->sd->dev, "no frame interval, FIM disabled\n");
     78		fim->enabled = false;
     79		return;
     80	}
     81
     82	fim->nominal = DIV_ROUND_CLOSEST_ULL(1000000ULL * (u64)fi->numerator,
     83					     fi->denominator);
     84
     85	dev_dbg(fim->sd->dev, "FI=%lu usec\n", fim->nominal);
     86}
     87
     88static void reset_fim(struct imx_media_fim *fim, bool curval)
     89{
     90	struct v4l2_ctrl *icap_chan = fim->icap_ctrl[FIM_CL_ICAP_CHANNEL];
     91	struct v4l2_ctrl *icap_edge = fim->icap_ctrl[FIM_CL_ICAP_EDGE];
     92	struct v4l2_ctrl *en = fim->ctrl[FIM_CL_ENABLE];
     93	struct v4l2_ctrl *num = fim->ctrl[FIM_CL_NUM];
     94	struct v4l2_ctrl *skip = fim->ctrl[FIM_CL_NUM_SKIP];
     95	struct v4l2_ctrl *tol_min = fim->ctrl[FIM_CL_TOLERANCE_MIN];
     96	struct v4l2_ctrl *tol_max = fim->ctrl[FIM_CL_TOLERANCE_MAX];
     97
     98	if (curval) {
     99		fim->enabled = en->cur.val;
    100		fim->icap_flags = icap_edge->cur.val;
    101		fim->icap_channel = icap_chan->cur.val;
    102		fim->num_avg = num->cur.val;
    103		fim->num_skip = skip->cur.val;
    104		fim->tolerance_min = tol_min->cur.val;
    105		fim->tolerance_max = tol_max->cur.val;
    106	} else {
    107		fim->enabled = en->val;
    108		fim->icap_flags = icap_edge->val;
    109		fim->icap_channel = icap_chan->val;
    110		fim->num_avg = num->val;
    111		fim->num_skip = skip->val;
    112		fim->tolerance_min = tol_min->val;
    113		fim->tolerance_max = tol_max->val;
    114	}
    115
    116	/* disable tolerance range if max <= min */
    117	if (fim->tolerance_max <= fim->tolerance_min)
    118		fim->tolerance_max = 0;
    119
    120	/* num_skip must be >= 1 if input capture not used */
    121	if (!icap_enabled(fim))
    122		fim->num_skip = max_t(int, fim->num_skip, 1);
    123
    124	fim->counter = -fim->num_skip;
    125	fim->sum = 0;
    126}
    127
    128static void send_fim_event(struct imx_media_fim *fim, unsigned long error)
    129{
    130	static const struct v4l2_event ev = {
    131		.type = V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR,
    132	};
    133
    134	v4l2_subdev_notify_event(fim->sd, &ev);
    135}
    136
    137/*
    138 * Monitor an averaged frame interval. If the average deviates too much
    139 * from the nominal frame rate, send the frame interval error event. The
    140 * frame intervals are averaged in order to quiet noise from
    141 * (presumably random) interrupt latency.
    142 */
    143static void frame_interval_monitor(struct imx_media_fim *fim,
    144				   ktime_t timestamp)
    145{
    146	long long interval, error;
    147	unsigned long error_avg;
    148	bool send_event = false;
    149
    150	if (!fim->enabled || ++fim->counter <= 0)
    151		goto out_update_ts;
    152
    153	/* max error is less than l00µs, so use 32-bit division or fail */
    154	interval = ktime_to_ns(ktime_sub(timestamp, fim->last_ts));
    155	error = abs(interval - NSEC_PER_USEC * (u64)fim->nominal);
    156	if (error > U32_MAX)
    157		error = U32_MAX;
    158	else
    159		error = abs((u32)error / NSEC_PER_USEC);
    160
    161	if (fim->tolerance_max && error >= fim->tolerance_max) {
    162		dev_dbg(fim->sd->dev,
    163			"FIM: %llu ignored, out of tolerance bounds\n",
    164			error);
    165		fim->counter--;
    166		goto out_update_ts;
    167	}
    168
    169	fim->sum += error;
    170
    171	if (fim->counter == fim->num_avg) {
    172		error_avg = DIV_ROUND_CLOSEST(fim->sum, fim->num_avg);
    173
    174		if (error_avg > fim->tolerance_min)
    175			send_event = true;
    176
    177		dev_dbg(fim->sd->dev, "FIM: error: %lu usec%s\n",
    178			error_avg, send_event ? " (!!!)" : "");
    179
    180		fim->counter = 0;
    181		fim->sum = 0;
    182	}
    183
    184out_update_ts:
    185	fim->last_ts = timestamp;
    186	if (send_event)
    187		send_fim_event(fim, error_avg);
    188}
    189
    190#ifdef CONFIG_IMX_GPT_ICAP
    191/*
    192 * Input Capture method of measuring frame intervals. Not subject
    193 * to interrupt latency.
    194 */
    195static void fim_input_capture_handler(int channel, void *dev_id,
    196				      ktime_t timestamp)
    197{
    198	struct imx_media_fim *fim = dev_id;
    199	unsigned long flags;
    200
    201	spin_lock_irqsave(&fim->lock, flags);
    202
    203	frame_interval_monitor(fim, timestamp);
    204
    205	if (!completion_done(&fim->icap_first_event))
    206		complete(&fim->icap_first_event);
    207
    208	spin_unlock_irqrestore(&fim->lock, flags);
    209}
    210
    211static int fim_request_input_capture(struct imx_media_fim *fim)
    212{
    213	init_completion(&fim->icap_first_event);
    214
    215	return mxc_request_input_capture(fim->icap_channel,
    216					 fim_input_capture_handler,
    217					 fim->icap_flags, fim);
    218}
    219
    220static void fim_free_input_capture(struct imx_media_fim *fim)
    221{
    222	mxc_free_input_capture(fim->icap_channel, fim);
    223}
    224
    225#else /* CONFIG_IMX_GPT_ICAP */
    226
    227static int fim_request_input_capture(struct imx_media_fim *fim)
    228{
    229	return 0;
    230}
    231
    232static void fim_free_input_capture(struct imx_media_fim *fim)
    233{
    234}
    235
    236#endif /* CONFIG_IMX_GPT_ICAP */
    237
    238/*
    239 * In case we are monitoring the first frame interval after streamon
    240 * (when fim->num_skip = 0), we need a valid fim->last_ts before we
    241 * can begin. This only applies to the input capture method. It is not
    242 * possible to accurately measure the first FI after streamon using the
    243 * EOF method, so fim->num_skip minimum is set to 1 in that case, so this
    244 * function is a noop when the EOF method is used.
    245 */
    246static void fim_acquire_first_ts(struct imx_media_fim *fim)
    247{
    248	unsigned long ret;
    249
    250	if (!fim->enabled || fim->num_skip > 0)
    251		return;
    252
    253	ret = wait_for_completion_timeout(
    254		&fim->icap_first_event,
    255		msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
    256	if (ret == 0)
    257		v4l2_warn(fim->sd, "wait first icap event timeout\n");
    258}
    259
    260/* FIM Controls */
    261static int fim_s_ctrl(struct v4l2_ctrl *ctrl)
    262{
    263	struct imx_media_fim *fim = container_of(ctrl->handler,
    264						 struct imx_media_fim,
    265						 ctrl_handler);
    266	unsigned long flags;
    267	int ret = 0;
    268
    269	spin_lock_irqsave(&fim->lock, flags);
    270
    271	switch (ctrl->id) {
    272	case V4L2_CID_IMX_FIM_ENABLE:
    273		break;
    274	case V4L2_CID_IMX_FIM_ICAP_EDGE:
    275		if (fim->stream_on)
    276			ret = -EBUSY;
    277		break;
    278	default:
    279		ret = -EINVAL;
    280	}
    281
    282	if (!ret)
    283		reset_fim(fim, false);
    284
    285	spin_unlock_irqrestore(&fim->lock, flags);
    286	return ret;
    287}
    288
    289static const struct v4l2_ctrl_ops fim_ctrl_ops = {
    290	.s_ctrl = fim_s_ctrl,
    291};
    292
    293static const struct v4l2_ctrl_config fim_ctrl[] = {
    294	[FIM_CL_ENABLE] = {
    295		.ops = &fim_ctrl_ops,
    296		.id = V4L2_CID_IMX_FIM_ENABLE,
    297		.name = "FIM Enable",
    298		.type = V4L2_CTRL_TYPE_BOOLEAN,
    299		.def = FIM_CL_ENABLE_DEF,
    300		.min = 0,
    301		.max = 1,
    302		.step = 1,
    303	},
    304	[FIM_CL_NUM] = {
    305		.ops = &fim_ctrl_ops,
    306		.id = V4L2_CID_IMX_FIM_NUM,
    307		.name = "FIM Num Average",
    308		.type = V4L2_CTRL_TYPE_INTEGER,
    309		.def = FIM_CL_NUM_DEF,
    310		.min =  1, /* no averaging */
    311		.max = 64, /* average 64 frames */
    312		.step = 1,
    313	},
    314	[FIM_CL_TOLERANCE_MIN] = {
    315		.ops = &fim_ctrl_ops,
    316		.id = V4L2_CID_IMX_FIM_TOLERANCE_MIN,
    317		.name = "FIM Tolerance Min",
    318		.type = V4L2_CTRL_TYPE_INTEGER,
    319		.def = FIM_CL_TOLERANCE_MIN_DEF,
    320		.min =    2,
    321		.max =  200,
    322		.step =   1,
    323	},
    324	[FIM_CL_TOLERANCE_MAX] = {
    325		.ops = &fim_ctrl_ops,
    326		.id = V4L2_CID_IMX_FIM_TOLERANCE_MAX,
    327		.name = "FIM Tolerance Max",
    328		.type = V4L2_CTRL_TYPE_INTEGER,
    329		.def = FIM_CL_TOLERANCE_MAX_DEF,
    330		.min =    0,
    331		.max =  500,
    332		.step =   1,
    333	},
    334	[FIM_CL_NUM_SKIP] = {
    335		.ops = &fim_ctrl_ops,
    336		.id = V4L2_CID_IMX_FIM_NUM_SKIP,
    337		.name = "FIM Num Skip",
    338		.type = V4L2_CTRL_TYPE_INTEGER,
    339		.def = FIM_CL_NUM_SKIP_DEF,
    340		.min =   0, /* skip no frames */
    341		.max = 256, /* skip 256 frames */
    342		.step =  1,
    343	},
    344};
    345
    346static const struct v4l2_ctrl_config fim_icap_ctrl[] = {
    347	[FIM_CL_ICAP_EDGE] = {
    348		.ops = &fim_ctrl_ops,
    349		.id = V4L2_CID_IMX_FIM_ICAP_EDGE,
    350		.name = "FIM Input Capture Edge",
    351		.type = V4L2_CTRL_TYPE_INTEGER,
    352		.def =  IRQ_TYPE_NONE, /* input capture disabled by default */
    353		.min =  IRQ_TYPE_NONE,
    354		.max =  IRQ_TYPE_EDGE_BOTH,
    355		.step = 1,
    356	},
    357	[FIM_CL_ICAP_CHANNEL] = {
    358		.ops = &fim_ctrl_ops,
    359		.id = V4L2_CID_IMX_FIM_ICAP_CHANNEL,
    360		.name = "FIM Input Capture Channel",
    361		.type = V4L2_CTRL_TYPE_INTEGER,
    362		.def =  0,
    363		.min =  0,
    364		.max =  1,
    365		.step = 1,
    366	},
    367};
    368
    369static int init_fim_controls(struct imx_media_fim *fim)
    370{
    371	struct v4l2_ctrl_handler *hdlr = &fim->ctrl_handler;
    372	int i, ret;
    373
    374	v4l2_ctrl_handler_init(hdlr, FIM_NUM_CONTROLS + FIM_NUM_ICAP_CONTROLS);
    375
    376	for (i = 0; i < FIM_NUM_CONTROLS; i++)
    377		fim->ctrl[i] = v4l2_ctrl_new_custom(hdlr,
    378						    &fim_ctrl[i],
    379						    NULL);
    380	for (i = 0; i < FIM_NUM_ICAP_CONTROLS; i++)
    381		fim->icap_ctrl[i] = v4l2_ctrl_new_custom(hdlr,
    382							 &fim_icap_ctrl[i],
    383							 NULL);
    384	if (hdlr->error) {
    385		ret = hdlr->error;
    386		goto err_free;
    387	}
    388
    389	v4l2_ctrl_cluster(FIM_NUM_CONTROLS, fim->ctrl);
    390	v4l2_ctrl_cluster(FIM_NUM_ICAP_CONTROLS, fim->icap_ctrl);
    391
    392	return 0;
    393err_free:
    394	v4l2_ctrl_handler_free(hdlr);
    395	return ret;
    396}
    397
    398/*
    399 * Monitor frame intervals via EOF interrupt. This method is
    400 * subject to uncertainty errors introduced by interrupt latency.
    401 *
    402 * This is a noop if the Input Capture method is being used, since
    403 * the frame_interval_monitor() is called by the input capture event
    404 * callback handler in that case.
    405 */
    406void imx_media_fim_eof_monitor(struct imx_media_fim *fim, ktime_t timestamp)
    407{
    408	unsigned long flags;
    409
    410	spin_lock_irqsave(&fim->lock, flags);
    411
    412	if (!icap_enabled(fim))
    413		frame_interval_monitor(fim, timestamp);
    414
    415	spin_unlock_irqrestore(&fim->lock, flags);
    416}
    417
    418/* Called by the subdev in its s_stream callback */
    419int imx_media_fim_set_stream(struct imx_media_fim *fim,
    420			     const struct v4l2_fract *fi,
    421			     bool on)
    422{
    423	unsigned long flags;
    424	int ret = 0;
    425
    426	v4l2_ctrl_lock(fim->ctrl[FIM_CL_ENABLE]);
    427
    428	if (fim->stream_on == on)
    429		goto out;
    430
    431	if (on) {
    432		spin_lock_irqsave(&fim->lock, flags);
    433		reset_fim(fim, true);
    434		update_fim_nominal(fim, fi);
    435		spin_unlock_irqrestore(&fim->lock, flags);
    436
    437		if (icap_enabled(fim)) {
    438			ret = fim_request_input_capture(fim);
    439			if (ret)
    440				goto out;
    441			fim_acquire_first_ts(fim);
    442		}
    443	} else {
    444		if (icap_enabled(fim))
    445			fim_free_input_capture(fim);
    446	}
    447
    448	fim->stream_on = on;
    449out:
    450	v4l2_ctrl_unlock(fim->ctrl[FIM_CL_ENABLE]);
    451	return ret;
    452}
    453
    454int imx_media_fim_add_controls(struct imx_media_fim *fim)
    455{
    456	/* add the FIM controls to the calling subdev ctrl handler */
    457	return v4l2_ctrl_add_handler(fim->sd->ctrl_handler,
    458				     &fim->ctrl_handler, NULL, false);
    459}
    460
    461/* Called by the subdev in its subdev registered callback */
    462struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd)
    463{
    464	struct imx_media_fim *fim;
    465	int ret;
    466
    467	fim = devm_kzalloc(sd->dev, sizeof(*fim), GFP_KERNEL);
    468	if (!fim)
    469		return ERR_PTR(-ENOMEM);
    470
    471	fim->sd = sd;
    472
    473	spin_lock_init(&fim->lock);
    474
    475	ret = init_fim_controls(fim);
    476	if (ret)
    477		return ERR_PTR(ret);
    478
    479	return fim;
    480}
    481
    482void imx_media_fim_free(struct imx_media_fim *fim)
    483{
    484	v4l2_ctrl_handler_free(&fim->ctrl_handler);
    485}