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

gpio-siox.c (6491B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2015-2018 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
      4 */
      5
      6#include <linux/module.h>
      7#include <linux/siox.h>
      8#include <linux/gpio/driver.h>
      9#include <linux/of.h>
     10
     11struct gpio_siox_ddata {
     12	struct gpio_chip gchip;
     13	struct irq_chip ichip;
     14	struct mutex lock;
     15	u8 setdata[1];
     16	u8 getdata[3];
     17
     18	raw_spinlock_t irqlock;
     19	u32 irq_enable;
     20	u32 irq_status;
     21	u32 irq_type[20];
     22};
     23
     24/*
     25 * Note that this callback only sets the value that is clocked out in the next
     26 * cycle.
     27 */
     28static int gpio_siox_set_data(struct siox_device *sdevice, u8 status, u8 buf[])
     29{
     30	struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
     31
     32	mutex_lock(&ddata->lock);
     33	buf[0] = ddata->setdata[0];
     34	mutex_unlock(&ddata->lock);
     35
     36	return 0;
     37}
     38
     39static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
     40{
     41	struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
     42	size_t offset;
     43	u32 trigger;
     44
     45	mutex_lock(&ddata->lock);
     46
     47	raw_spin_lock_irq(&ddata->irqlock);
     48
     49	for (offset = 0; offset < 12; ++offset) {
     50		unsigned int bitpos = 11 - offset;
     51		unsigned int gpiolevel = buf[bitpos / 8] & (1 << bitpos % 8);
     52		unsigned int prev_level =
     53			ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
     54		u32 irq_type = ddata->irq_type[offset];
     55
     56		if (gpiolevel) {
     57			if ((irq_type & IRQ_TYPE_LEVEL_HIGH) ||
     58			    ((irq_type & IRQ_TYPE_EDGE_RISING) && !prev_level))
     59				ddata->irq_status |= 1 << offset;
     60		} else {
     61			if ((irq_type & IRQ_TYPE_LEVEL_LOW) ||
     62			    ((irq_type & IRQ_TYPE_EDGE_FALLING) && prev_level))
     63				ddata->irq_status |= 1 << offset;
     64		}
     65	}
     66
     67	trigger = ddata->irq_status & ddata->irq_enable;
     68
     69	raw_spin_unlock_irq(&ddata->irqlock);
     70
     71	ddata->getdata[0] = buf[0];
     72	ddata->getdata[1] = buf[1];
     73	ddata->getdata[2] = buf[2];
     74
     75	mutex_unlock(&ddata->lock);
     76
     77	for (offset = 0; offset < 12; ++offset) {
     78		if (trigger & (1 << offset)) {
     79			struct irq_domain *irqdomain = ddata->gchip.irq.domain;
     80			unsigned int irq = irq_find_mapping(irqdomain, offset);
     81
     82			/*
     83			 * Conceptually handle_nested_irq should call the flow
     84			 * handler of the irq chip. But it doesn't, so we have
     85			 * to clean the irq_status here.
     86			 */
     87			raw_spin_lock_irq(&ddata->irqlock);
     88			ddata->irq_status &= ~(1 << offset);
     89			raw_spin_unlock_irq(&ddata->irqlock);
     90
     91			handle_nested_irq(irq);
     92		}
     93	}
     94
     95	return 0;
     96}
     97
     98static void gpio_siox_irq_ack(struct irq_data *d)
     99{
    100	struct irq_chip *ic = irq_data_get_irq_chip(d);
    101	struct gpio_siox_ddata *ddata =
    102		container_of(ic, struct gpio_siox_ddata, ichip);
    103
    104	raw_spin_lock(&ddata->irqlock);
    105	ddata->irq_status &= ~(1 << d->hwirq);
    106	raw_spin_unlock(&ddata->irqlock);
    107}
    108
    109static void gpio_siox_irq_mask(struct irq_data *d)
    110{
    111	struct irq_chip *ic = irq_data_get_irq_chip(d);
    112	struct gpio_siox_ddata *ddata =
    113		container_of(ic, struct gpio_siox_ddata, ichip);
    114
    115	raw_spin_lock(&ddata->irqlock);
    116	ddata->irq_enable &= ~(1 << d->hwirq);
    117	raw_spin_unlock(&ddata->irqlock);
    118}
    119
    120static void gpio_siox_irq_unmask(struct irq_data *d)
    121{
    122	struct irq_chip *ic = irq_data_get_irq_chip(d);
    123	struct gpio_siox_ddata *ddata =
    124		container_of(ic, struct gpio_siox_ddata, ichip);
    125
    126	raw_spin_lock(&ddata->irqlock);
    127	ddata->irq_enable |= 1 << d->hwirq;
    128	raw_spin_unlock(&ddata->irqlock);
    129}
    130
    131static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
    132{
    133	struct irq_chip *ic = irq_data_get_irq_chip(d);
    134	struct gpio_siox_ddata *ddata =
    135		container_of(ic, struct gpio_siox_ddata, ichip);
    136
    137	raw_spin_lock(&ddata->irqlock);
    138	ddata->irq_type[d->hwirq] = type;
    139	raw_spin_unlock(&ddata->irqlock);
    140
    141	return 0;
    142}
    143
    144static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset)
    145{
    146	struct gpio_siox_ddata *ddata =
    147		container_of(chip, struct gpio_siox_ddata, gchip);
    148	int ret;
    149
    150	mutex_lock(&ddata->lock);
    151
    152	if (offset >= 12) {
    153		unsigned int bitpos = 19 - offset;
    154
    155		ret = ddata->setdata[0] & (1 << bitpos);
    156	} else {
    157		unsigned int bitpos = 11 - offset;
    158
    159		ret = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
    160	}
    161
    162	mutex_unlock(&ddata->lock);
    163
    164	return ret;
    165}
    166
    167static void gpio_siox_set(struct gpio_chip *chip,
    168			  unsigned int offset, int value)
    169{
    170	struct gpio_siox_ddata *ddata =
    171		container_of(chip, struct gpio_siox_ddata, gchip);
    172	u8 mask = 1 << (19 - offset);
    173
    174	mutex_lock(&ddata->lock);
    175
    176	if (value)
    177		ddata->setdata[0] |= mask;
    178	else
    179		ddata->setdata[0] &= ~mask;
    180
    181	mutex_unlock(&ddata->lock);
    182}
    183
    184static int gpio_siox_direction_input(struct gpio_chip *chip,
    185				     unsigned int offset)
    186{
    187	if (offset >= 12)
    188		return -EINVAL;
    189
    190	return 0;
    191}
    192
    193static int gpio_siox_direction_output(struct gpio_chip *chip,
    194				      unsigned int offset, int value)
    195{
    196	if (offset < 12)
    197		return -EINVAL;
    198
    199	gpio_siox_set(chip, offset, value);
    200	return 0;
    201}
    202
    203static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset)
    204{
    205	if (offset < 12)
    206		return GPIO_LINE_DIRECTION_IN;
    207	else
    208		return GPIO_LINE_DIRECTION_OUT;
    209}
    210
    211static int gpio_siox_probe(struct siox_device *sdevice)
    212{
    213	struct gpio_siox_ddata *ddata;
    214	struct gpio_irq_chip *girq;
    215	struct device *dev = &sdevice->dev;
    216	int ret;
    217
    218	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
    219	if (!ddata)
    220		return -ENOMEM;
    221
    222	dev_set_drvdata(dev, ddata);
    223
    224	mutex_init(&ddata->lock);
    225	raw_spin_lock_init(&ddata->irqlock);
    226
    227	ddata->gchip.base = -1;
    228	ddata->gchip.can_sleep = 1;
    229	ddata->gchip.parent = dev;
    230	ddata->gchip.owner = THIS_MODULE;
    231	ddata->gchip.get = gpio_siox_get;
    232	ddata->gchip.set = gpio_siox_set;
    233	ddata->gchip.direction_input = gpio_siox_direction_input;
    234	ddata->gchip.direction_output = gpio_siox_direction_output;
    235	ddata->gchip.get_direction = gpio_siox_get_direction;
    236	ddata->gchip.ngpio = 20;
    237
    238	ddata->ichip.name = "siox-gpio";
    239	ddata->ichip.irq_ack = gpio_siox_irq_ack;
    240	ddata->ichip.irq_mask = gpio_siox_irq_mask;
    241	ddata->ichip.irq_unmask = gpio_siox_irq_unmask;
    242	ddata->ichip.irq_set_type = gpio_siox_irq_set_type;
    243
    244	girq = &ddata->gchip.irq;
    245	girq->chip = &ddata->ichip;
    246	girq->default_type = IRQ_TYPE_NONE;
    247	girq->handler = handle_level_irq;
    248	girq->threaded = true;
    249
    250	ret = devm_gpiochip_add_data(dev, &ddata->gchip, NULL);
    251	if (ret)
    252		dev_err(dev, "Failed to register gpio chip (%d)\n", ret);
    253
    254	return ret;
    255}
    256
    257static struct siox_driver gpio_siox_driver = {
    258	.probe = gpio_siox_probe,
    259	.set_data = gpio_siox_set_data,
    260	.get_data = gpio_siox_get_data,
    261	.driver = {
    262		.name = "gpio-siox",
    263	},
    264};
    265module_siox_driver(gpio_siox_driver);
    266
    267MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
    268MODULE_DESCRIPTION("SIOX gpio driver");
    269MODULE_LICENSE("GPL v2");