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

interrupt-cnt.c (6157B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2021 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
      4 */
      5
      6#include <linux/counter.h>
      7#include <linux/gpio/consumer.h>
      8#include <linux/interrupt.h>
      9#include <linux/irq.h>
     10#include <linux/mod_devicetable.h>
     11#include <linux/module.h>
     12#include <linux/platform_device.h>
     13#include <linux/types.h>
     14
     15#define INTERRUPT_CNT_NAME "interrupt-cnt"
     16
     17struct interrupt_cnt_priv {
     18	atomic_t count;
     19	struct gpio_desc *gpio;
     20	int irq;
     21	bool enabled;
     22	struct counter_signal signals;
     23	struct counter_synapse synapses;
     24	struct counter_count cnts;
     25};
     26
     27static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id)
     28{
     29	struct counter_device *counter = dev_id;
     30	struct interrupt_cnt_priv *priv = counter_priv(counter);
     31
     32	atomic_inc(&priv->count);
     33
     34	counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE, 0);
     35
     36	return IRQ_HANDLED;
     37}
     38
     39static int interrupt_cnt_enable_read(struct counter_device *counter,
     40				     struct counter_count *count, u8 *enable)
     41{
     42	struct interrupt_cnt_priv *priv = counter_priv(counter);
     43
     44	*enable = priv->enabled;
     45
     46	return 0;
     47}
     48
     49static int interrupt_cnt_enable_write(struct counter_device *counter,
     50				      struct counter_count *count, u8 enable)
     51{
     52	struct interrupt_cnt_priv *priv = counter_priv(counter);
     53
     54	if (priv->enabled == enable)
     55		return 0;
     56
     57	if (enable) {
     58		priv->enabled = true;
     59		enable_irq(priv->irq);
     60	} else {
     61		disable_irq(priv->irq);
     62		priv->enabled = false;
     63	}
     64
     65	return 0;
     66}
     67
     68static struct counter_comp interrupt_cnt_ext[] = {
     69	COUNTER_COMP_ENABLE(interrupt_cnt_enable_read,
     70			    interrupt_cnt_enable_write),
     71};
     72
     73static const enum counter_synapse_action interrupt_cnt_synapse_actions[] = {
     74	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
     75};
     76
     77static int interrupt_cnt_action_read(struct counter_device *counter,
     78				     struct counter_count *count,
     79				     struct counter_synapse *synapse,
     80				     enum counter_synapse_action *action)
     81{
     82	*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
     83
     84	return 0;
     85}
     86
     87static int interrupt_cnt_read(struct counter_device *counter,
     88			      struct counter_count *count, u64 *val)
     89{
     90	struct interrupt_cnt_priv *priv = counter_priv(counter);
     91
     92	*val = atomic_read(&priv->count);
     93
     94	return 0;
     95}
     96
     97static int interrupt_cnt_write(struct counter_device *counter,
     98			       struct counter_count *count, const u64 val)
     99{
    100	struct interrupt_cnt_priv *priv = counter_priv(counter);
    101
    102	if (val != (typeof(priv->count.counter))val)
    103		return -ERANGE;
    104
    105	atomic_set(&priv->count, val);
    106
    107	return 0;
    108}
    109
    110static const enum counter_function interrupt_cnt_functions[] = {
    111	COUNTER_FUNCTION_INCREASE,
    112};
    113
    114static int interrupt_cnt_function_read(struct counter_device *counter,
    115				       struct counter_count *count,
    116				       enum counter_function *function)
    117{
    118	*function = COUNTER_FUNCTION_INCREASE;
    119
    120	return 0;
    121}
    122
    123static int interrupt_cnt_signal_read(struct counter_device *counter,
    124				     struct counter_signal *signal,
    125				     enum counter_signal_level *level)
    126{
    127	struct interrupt_cnt_priv *priv = counter_priv(counter);
    128	int ret;
    129
    130	if (!priv->gpio)
    131		return -EINVAL;
    132
    133	ret = gpiod_get_value(priv->gpio);
    134	if (ret < 0)
    135		return ret;
    136
    137	*level = ret ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
    138
    139	return 0;
    140}
    141
    142static const struct counter_ops interrupt_cnt_ops = {
    143	.action_read = interrupt_cnt_action_read,
    144	.count_read = interrupt_cnt_read,
    145	.count_write = interrupt_cnt_write,
    146	.function_read = interrupt_cnt_function_read,
    147	.signal_read  = interrupt_cnt_signal_read,
    148};
    149
    150static int interrupt_cnt_probe(struct platform_device *pdev)
    151{
    152	struct device *dev = &pdev->dev;
    153	struct counter_device *counter;
    154	struct interrupt_cnt_priv *priv;
    155	int ret;
    156
    157	counter = devm_counter_alloc(dev, sizeof(*priv));
    158	if (!counter)
    159		return -ENOMEM;
    160	priv = counter_priv(counter);
    161
    162	priv->irq = platform_get_irq_optional(pdev,  0);
    163	if (priv->irq == -ENXIO)
    164		priv->irq = 0;
    165	else if (priv->irq < 0)
    166		return dev_err_probe(dev, priv->irq, "failed to get IRQ\n");
    167
    168	priv->gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_IN);
    169	if (IS_ERR(priv->gpio))
    170		return dev_err_probe(dev, PTR_ERR(priv->gpio), "failed to get GPIO\n");
    171
    172	if (!priv->irq && !priv->gpio) {
    173		dev_err(dev, "IRQ and GPIO are not found. At least one source should be provided\n");
    174		return -ENODEV;
    175	}
    176
    177	if (!priv->irq) {
    178		int irq = gpiod_to_irq(priv->gpio);
    179
    180		if (irq < 0)
    181			return dev_err_probe(dev, irq, "failed to get IRQ from GPIO\n");
    182
    183		priv->irq = irq;
    184	}
    185
    186	priv->signals.name = devm_kasprintf(dev, GFP_KERNEL, "IRQ %d",
    187					    priv->irq);
    188	if (!priv->signals.name)
    189		return -ENOMEM;
    190
    191	counter->signals = &priv->signals;
    192	counter->num_signals = 1;
    193
    194	priv->synapses.actions_list = interrupt_cnt_synapse_actions;
    195	priv->synapses.num_actions = ARRAY_SIZE(interrupt_cnt_synapse_actions);
    196	priv->synapses.signal = &priv->signals;
    197
    198	priv->cnts.name = "Channel 0 Count";
    199	priv->cnts.functions_list = interrupt_cnt_functions;
    200	priv->cnts.num_functions = ARRAY_SIZE(interrupt_cnt_functions);
    201	priv->cnts.synapses = &priv->synapses;
    202	priv->cnts.num_synapses = 1;
    203	priv->cnts.ext = interrupt_cnt_ext;
    204	priv->cnts.num_ext = ARRAY_SIZE(interrupt_cnt_ext);
    205
    206	counter->name = dev_name(dev);
    207	counter->parent = dev;
    208	counter->ops = &interrupt_cnt_ops;
    209	counter->counts = &priv->cnts;
    210	counter->num_counts = 1;
    211
    212	irq_set_status_flags(priv->irq, IRQ_NOAUTOEN);
    213	ret = devm_request_irq(dev, priv->irq, interrupt_cnt_isr,
    214			       IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
    215			       dev_name(dev), counter);
    216	if (ret)
    217		return ret;
    218
    219	ret = devm_counter_add(dev, counter);
    220	if (ret < 0)
    221		return dev_err_probe(dev, ret, "Failed to add counter\n");
    222
    223	return 0;
    224}
    225
    226static const struct of_device_id interrupt_cnt_of_match[] = {
    227	{ .compatible = "interrupt-counter", },
    228	{}
    229};
    230MODULE_DEVICE_TABLE(of, interrupt_cnt_of_match);
    231
    232static struct platform_driver interrupt_cnt_driver = {
    233	.probe = interrupt_cnt_probe,
    234	.driver = {
    235		.name = INTERRUPT_CNT_NAME,
    236		.of_match_table = interrupt_cnt_of_match,
    237	},
    238};
    239module_platform_driver(interrupt_cnt_driver);
    240
    241MODULE_ALIAS("platform:interrupt-counter");
    242MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");
    243MODULE_DESCRIPTION("Interrupt counter driver");
    244MODULE_LICENSE("GPL v2");