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

stx104.c (9566B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * IIO driver for the Apex Embedded Systems STX104
      4 * Copyright (C) 2016 William Breathitt Gray
      5 */
      6#include <linux/bitops.h>
      7#include <linux/device.h>
      8#include <linux/errno.h>
      9#include <linux/gpio/driver.h>
     10#include <linux/iio/iio.h>
     11#include <linux/iio/types.h>
     12#include <linux/io.h>
     13#include <linux/ioport.h>
     14#include <linux/isa.h>
     15#include <linux/kernel.h>
     16#include <linux/module.h>
     17#include <linux/moduleparam.h>
     18#include <linux/spinlock.h>
     19
     20#define STX104_OUT_CHAN(chan) {				\
     21	.type = IIO_VOLTAGE,				\
     22	.channel = chan,				\
     23	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
     24	.indexed = 1,					\
     25	.output = 1					\
     26}
     27#define STX104_IN_CHAN(chan, diff) {					\
     28	.type = IIO_VOLTAGE,						\
     29	.channel = chan,						\
     30	.channel2 = chan,						\
     31	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) |	\
     32		BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),	\
     33	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
     34	.indexed = 1,							\
     35	.differential = diff						\
     36}
     37
     38#define STX104_NUM_OUT_CHAN 2
     39
     40#define STX104_EXTENT 16
     41
     42static unsigned int base[max_num_isa_dev(STX104_EXTENT)];
     43static unsigned int num_stx104;
     44module_param_hw_array(base, uint, ioport, &num_stx104, 0);
     45MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
     46
     47/**
     48 * struct stx104_iio - IIO device private data structure
     49 * @chan_out_states:	channels' output states
     50 * @base:		base port address of the IIO device
     51 */
     52struct stx104_iio {
     53	unsigned int chan_out_states[STX104_NUM_OUT_CHAN];
     54	unsigned int base;
     55};
     56
     57/**
     58 * struct stx104_gpio - GPIO device private data structure
     59 * @chip:	instance of the gpio_chip
     60 * @lock:	synchronization lock to prevent I/O race conditions
     61 * @base:	base port address of the GPIO device
     62 * @out_state:	output bits state
     63 */
     64struct stx104_gpio {
     65	struct gpio_chip chip;
     66	spinlock_t lock;
     67	unsigned int base;
     68	unsigned int out_state;
     69};
     70
     71static int stx104_read_raw(struct iio_dev *indio_dev,
     72	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
     73{
     74	struct stx104_iio *const priv = iio_priv(indio_dev);
     75	unsigned int adc_config;
     76	int adbu;
     77	int gain;
     78
     79	switch (mask) {
     80	case IIO_CHAN_INFO_HARDWAREGAIN:
     81		/* get gain configuration */
     82		adc_config = inb(priv->base + 11);
     83		gain = adc_config & 0x3;
     84
     85		*val = 1 << gain;
     86		return IIO_VAL_INT;
     87	case IIO_CHAN_INFO_RAW:
     88		if (chan->output) {
     89			*val = priv->chan_out_states[chan->channel];
     90			return IIO_VAL_INT;
     91		}
     92
     93		/* select ADC channel */
     94		outb(chan->channel | (chan->channel << 4), priv->base + 2);
     95
     96		/* trigger ADC sample capture and wait for completion */
     97		outb(0, priv->base);
     98		while (inb(priv->base + 8) & BIT(7));
     99
    100		*val = inw(priv->base);
    101		return IIO_VAL_INT;
    102	case IIO_CHAN_INFO_OFFSET:
    103		/* get ADC bipolar/unipolar configuration */
    104		adc_config = inb(priv->base + 11);
    105		adbu = !(adc_config & BIT(2));
    106
    107		*val = -32768 * adbu;
    108		return IIO_VAL_INT;
    109	case IIO_CHAN_INFO_SCALE:
    110		/* get ADC bipolar/unipolar and gain configuration */
    111		adc_config = inb(priv->base + 11);
    112		adbu = !(adc_config & BIT(2));
    113		gain = adc_config & 0x3;
    114
    115		*val = 5;
    116		*val2 = 15 - adbu + gain;
    117		return IIO_VAL_FRACTIONAL_LOG2;
    118	}
    119
    120	return -EINVAL;
    121}
    122
    123static int stx104_write_raw(struct iio_dev *indio_dev,
    124	struct iio_chan_spec const *chan, int val, int val2, long mask)
    125{
    126	struct stx104_iio *const priv = iio_priv(indio_dev);
    127
    128	switch (mask) {
    129	case IIO_CHAN_INFO_HARDWAREGAIN:
    130		/* Only four gain states (x1, x2, x4, x8) */
    131		switch (val) {
    132		case 1:
    133			outb(0, priv->base + 11);
    134			break;
    135		case 2:
    136			outb(1, priv->base + 11);
    137			break;
    138		case 4:
    139			outb(2, priv->base + 11);
    140			break;
    141		case 8:
    142			outb(3, priv->base + 11);
    143			break;
    144		default:
    145			return -EINVAL;
    146		}
    147
    148		return 0;
    149	case IIO_CHAN_INFO_RAW:
    150		if (chan->output) {
    151			/* DAC can only accept up to a 16-bit value */
    152			if ((unsigned int)val > 65535)
    153				return -EINVAL;
    154
    155			priv->chan_out_states[chan->channel] = val;
    156			outw(val, priv->base + 4 + 2 * chan->channel);
    157
    158			return 0;
    159		}
    160		return -EINVAL;
    161	}
    162
    163	return -EINVAL;
    164}
    165
    166static const struct iio_info stx104_info = {
    167	.read_raw = stx104_read_raw,
    168	.write_raw = stx104_write_raw
    169};
    170
    171/* single-ended input channels configuration */
    172static const struct iio_chan_spec stx104_channels_sing[] = {
    173	STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
    174	STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0),
    175	STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0),
    176	STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0),
    177	STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0),
    178	STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0),
    179	STX104_IN_CHAN(15, 0)
    180};
    181/* differential input channels configuration */
    182static const struct iio_chan_spec stx104_channels_diff[] = {
    183	STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
    184	STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1),
    185	STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1),
    186	STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
    187};
    188
    189static int stx104_gpio_get_direction(struct gpio_chip *chip,
    190	unsigned int offset)
    191{
    192	/* GPIO 0-3 are input only, while the rest are output only */
    193	if (offset < 4)
    194		return 1;
    195
    196	return 0;
    197}
    198
    199static int stx104_gpio_direction_input(struct gpio_chip *chip,
    200	unsigned int offset)
    201{
    202	if (offset >= 4)
    203		return -EINVAL;
    204
    205	return 0;
    206}
    207
    208static int stx104_gpio_direction_output(struct gpio_chip *chip,
    209	unsigned int offset, int value)
    210{
    211	if (offset < 4)
    212		return -EINVAL;
    213
    214	chip->set(chip, offset, value);
    215	return 0;
    216}
    217
    218static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset)
    219{
    220	struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
    221
    222	if (offset >= 4)
    223		return -EINVAL;
    224
    225	return !!(inb(stx104gpio->base) & BIT(offset));
    226}
    227
    228static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
    229	unsigned long *bits)
    230{
    231	struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
    232
    233	*bits = inb(stx104gpio->base);
    234
    235	return 0;
    236}
    237
    238static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset,
    239	int value)
    240{
    241	struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
    242	const unsigned int mask = BIT(offset) >> 4;
    243	unsigned long flags;
    244
    245	if (offset < 4)
    246		return;
    247
    248	spin_lock_irqsave(&stx104gpio->lock, flags);
    249
    250	if (value)
    251		stx104gpio->out_state |= mask;
    252	else
    253		stx104gpio->out_state &= ~mask;
    254
    255	outb(stx104gpio->out_state, stx104gpio->base);
    256
    257	spin_unlock_irqrestore(&stx104gpio->lock, flags);
    258}
    259
    260#define STX104_NGPIO 8
    261static const char *stx104_names[STX104_NGPIO] = {
    262	"DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3"
    263};
    264
    265static void stx104_gpio_set_multiple(struct gpio_chip *chip,
    266	unsigned long *mask, unsigned long *bits)
    267{
    268	struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
    269	unsigned long flags;
    270
    271	/* verify masked GPIO are output */
    272	if (!(*mask & 0xF0))
    273		return;
    274
    275	*mask >>= 4;
    276	*bits >>= 4;
    277
    278	spin_lock_irqsave(&stx104gpio->lock, flags);
    279
    280	stx104gpio->out_state &= ~*mask;
    281	stx104gpio->out_state |= *mask & *bits;
    282	outb(stx104gpio->out_state, stx104gpio->base);
    283
    284	spin_unlock_irqrestore(&stx104gpio->lock, flags);
    285}
    286
    287static int stx104_probe(struct device *dev, unsigned int id)
    288{
    289	struct iio_dev *indio_dev;
    290	struct stx104_iio *priv;
    291	struct stx104_gpio *stx104gpio;
    292	int err;
    293
    294	indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
    295	if (!indio_dev)
    296		return -ENOMEM;
    297
    298	stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL);
    299	if (!stx104gpio)
    300		return -ENOMEM;
    301
    302	if (!devm_request_region(dev, base[id], STX104_EXTENT,
    303		dev_name(dev))) {
    304		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
    305			base[id], base[id] + STX104_EXTENT);
    306		return -EBUSY;
    307	}
    308
    309	indio_dev->info = &stx104_info;
    310	indio_dev->modes = INDIO_DIRECT_MODE;
    311
    312	/* determine if differential inputs */
    313	if (inb(base[id] + 8) & BIT(5)) {
    314		indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff);
    315		indio_dev->channels = stx104_channels_diff;
    316	} else {
    317		indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing);
    318		indio_dev->channels = stx104_channels_sing;
    319	}
    320
    321	indio_dev->name = dev_name(dev);
    322
    323	priv = iio_priv(indio_dev);
    324	priv->base = base[id];
    325
    326	/* configure device for software trigger operation */
    327	outb(0, base[id] + 9);
    328
    329	/* initialize gain setting to x1 */
    330	outb(0, base[id] + 11);
    331
    332	/* initialize DAC output to 0V */
    333	outw(0, base[id] + 4);
    334	outw(0, base[id] + 6);
    335
    336	stx104gpio->chip.label = dev_name(dev);
    337	stx104gpio->chip.parent = dev;
    338	stx104gpio->chip.owner = THIS_MODULE;
    339	stx104gpio->chip.base = -1;
    340	stx104gpio->chip.ngpio = STX104_NGPIO;
    341	stx104gpio->chip.names = stx104_names;
    342	stx104gpio->chip.get_direction = stx104_gpio_get_direction;
    343	stx104gpio->chip.direction_input = stx104_gpio_direction_input;
    344	stx104gpio->chip.direction_output = stx104_gpio_direction_output;
    345	stx104gpio->chip.get = stx104_gpio_get;
    346	stx104gpio->chip.get_multiple = stx104_gpio_get_multiple;
    347	stx104gpio->chip.set = stx104_gpio_set;
    348	stx104gpio->chip.set_multiple = stx104_gpio_set_multiple;
    349	stx104gpio->base = base[id] + 3;
    350	stx104gpio->out_state = 0x0;
    351
    352	spin_lock_init(&stx104gpio->lock);
    353
    354	err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio);
    355	if (err) {
    356		dev_err(dev, "GPIO registering failed (%d)\n", err);
    357		return err;
    358	}
    359
    360	return devm_iio_device_register(dev, indio_dev);
    361}
    362
    363static struct isa_driver stx104_driver = {
    364	.probe = stx104_probe,
    365	.driver = {
    366		.name = "stx104"
    367	},
    368};
    369
    370module_isa_driver(stx104_driver, num_stx104);
    371
    372MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
    373MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver");
    374MODULE_LICENSE("GPL v2");