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

das08.c (13079B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * comedi/drivers/das08.c
      4 * comedi module for common DAS08 support (used by ISA/PCI/PCMCIA drivers)
      5 *
      6 * COMEDI - Linux Control and Measurement Device Interface
      7 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
      8 * Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
      9 * Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/comedi/comedidev.h>
     14#include <linux/comedi/comedi_8255.h>
     15#include <linux/comedi/comedi_8254.h>
     16
     17#include "das08.h"
     18
     19/*
     20 * Data format of DAS08_AI_LSB_REG and DAS08_AI_MSB_REG depends on
     21 * 'ai_encoding' member of board structure:
     22 *
     23 * das08_encode12     : DATA[11..4] = MSB[7..0], DATA[3..0] = LSB[7..4].
     24 * das08_pcm_encode12 : DATA[11..8] = MSB[3..0], DATA[7..9] = LSB[7..0].
     25 * das08_encode16     : SIGN = MSB[7], MAGNITUDE[14..8] = MSB[6..0],
     26 *                      MAGNITUDE[7..0] = LSB[7..0].
     27 *                      SIGN==0 for negative input, SIGN==1 for positive input.
     28 *                      Note: when read a second time after conversion
     29 *                            complete, MSB[7] is an "over-range" bit.
     30 */
     31#define DAS08_AI_LSB_REG	0x00	/* (R) AI least significant bits */
     32#define DAS08_AI_MSB_REG	0x01	/* (R) AI most significant bits */
     33#define DAS08_AI_TRIG_REG	0x01	/* (W) AI software trigger */
     34#define DAS08_STATUS_REG	0x02	/* (R) status */
     35#define DAS08_STATUS_AI_BUSY	BIT(7)	/* AI conversion in progress */
     36/*
     37 * The IRQ status bit is set to 1 by a rising edge on the external interrupt
     38 * input (which may be jumpered to the pacer output).  It is cleared by
     39 * setting the INTE control bit to 0.  Not present on "JR" boards.
     40 */
     41#define DAS08_STATUS_IRQ	BIT(3)	/* latched interrupt input */
     42/* digital inputs (not "JR" boards) */
     43#define DAS08_STATUS_DI(x)	(((x) & 0x70) >> 4)
     44#define DAS08_CONTROL_REG	0x02	/* (W) control */
     45/*
     46 * Note: The AI multiplexor channel can also be read from status register using
     47 * the same mask.
     48 */
     49#define DAS08_CONTROL_MUX_MASK	0x7	/* multiplexor channel mask */
     50#define DAS08_CONTROL_MUX(x)	((x) & DAS08_CONTROL_MUX_MASK) /* mux channel */
     51#define DAS08_CONTROL_INTE	BIT(3)	/* interrupt enable (not "JR" boards) */
     52#define DAS08_CONTROL_DO_MASK	0xf0	/* digital outputs mask (not "JR") */
     53/* digital outputs (not "JR" boards) */
     54#define DAS08_CONTROL_DO(x)	(((x) << 4) & DAS08_CONTROL_DO_MASK)
     55/*
     56 * (R/W) programmable AI gain ("PGx" and "AOx" boards):
     57 * + bits 3..0 (R/W) show/set the gain for the current AI mux channel
     58 * + bits 6..4 (R) show the current AI mux channel
     59 * + bit 7 (R) not unused
     60 */
     61#define DAS08_GAIN_REG		0x03
     62
     63#define DAS08JR_DI_REG		0x03	/* (R) digital inputs ("JR" boards) */
     64#define DAS08JR_DO_REG		0x03	/* (W) digital outputs ("JR" boards) */
     65/* (W) analog output l.s.b. registers for 2 channels ("JR" boards) */
     66#define DAS08JR_AO_LSB_REG(x)	((x) ? 0x06 : 0x04)
     67/* (W) analog output m.s.b. registers for 2 channels ("JR" boards) */
     68#define DAS08JR_AO_MSB_REG(x)	((x) ? 0x07 : 0x05)
     69/*
     70 * (R) update analog outputs ("JR" boards set for simultaneous output)
     71 *     (same register as digital inputs)
     72 */
     73#define DAS08JR_AO_UPDATE_REG	0x03
     74
     75/* (W) analog output l.s.b. registers for 2 channels ("AOx" boards) */
     76#define DAS08AOX_AO_LSB_REG(x)	((x) ? 0x0a : 0x08)
     77/* (W) analog output m.s.b. registers for 2 channels ("AOx" boards) */
     78#define DAS08AOX_AO_MSB_REG(x)	((x) ? 0x0b : 0x09)
     79/*
     80 * (R) update analog outputs ("AOx" boards set for simultaneous output)
     81 *     (any of the analog output registers could be used for this)
     82 */
     83#define DAS08AOX_AO_UPDATE_REG	0x08
     84
     85/* gainlist same as _pgx_ below */
     86
     87static const struct comedi_lrange das08_pgl_ai_range = {
     88	9, {
     89		BIP_RANGE(10),
     90		BIP_RANGE(5),
     91		BIP_RANGE(2.5),
     92		BIP_RANGE(1.25),
     93		BIP_RANGE(0.625),
     94		UNI_RANGE(10),
     95		UNI_RANGE(5),
     96		UNI_RANGE(2.5),
     97		UNI_RANGE(1.25)
     98	}
     99};
    100
    101static const struct comedi_lrange das08_pgh_ai_range = {
    102	12, {
    103		BIP_RANGE(10),
    104		BIP_RANGE(5),
    105		BIP_RANGE(1),
    106		BIP_RANGE(0.5),
    107		BIP_RANGE(0.1),
    108		BIP_RANGE(0.05),
    109		BIP_RANGE(0.01),
    110		BIP_RANGE(0.005),
    111		UNI_RANGE(10),
    112		UNI_RANGE(1),
    113		UNI_RANGE(0.1),
    114		UNI_RANGE(0.01)
    115	}
    116};
    117
    118static const struct comedi_lrange das08_pgm_ai_range = {
    119	9, {
    120		BIP_RANGE(10),
    121		BIP_RANGE(5),
    122		BIP_RANGE(0.5),
    123		BIP_RANGE(0.05),
    124		BIP_RANGE(0.01),
    125		UNI_RANGE(10),
    126		UNI_RANGE(1),
    127		UNI_RANGE(0.1),
    128		UNI_RANGE(0.01)
    129	}
    130};
    131
    132static const struct comedi_lrange *const das08_ai_lranges[] = {
    133	[das08_pg_none]		= &range_unknown,
    134	[das08_bipolar5]	= &range_bipolar5,
    135	[das08_pgh]		= &das08_pgh_ai_range,
    136	[das08_pgl]		= &das08_pgl_ai_range,
    137	[das08_pgm]		= &das08_pgm_ai_range,
    138};
    139
    140static const int das08_pgh_ai_gainlist[] = {
    141	8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
    142};
    143static const int das08_pgl_ai_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
    144static const int das08_pgm_ai_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
    145
    146static const int *const das08_ai_gainlists[] = {
    147	[das08_pg_none]		= NULL,
    148	[das08_bipolar5]	= NULL,
    149	[das08_pgh]		= das08_pgh_ai_gainlist,
    150	[das08_pgl]		= das08_pgl_ai_gainlist,
    151	[das08_pgm]		= das08_pgm_ai_gainlist,
    152};
    153
    154static int das08_ai_eoc(struct comedi_device *dev,
    155			struct comedi_subdevice *s,
    156			struct comedi_insn *insn,
    157			unsigned long context)
    158{
    159	unsigned int status;
    160
    161	status = inb(dev->iobase + DAS08_STATUS_REG);
    162	if ((status & DAS08_STATUS_AI_BUSY) == 0)
    163		return 0;
    164	return -EBUSY;
    165}
    166
    167static int das08_ai_insn_read(struct comedi_device *dev,
    168			      struct comedi_subdevice *s,
    169			      struct comedi_insn *insn, unsigned int *data)
    170{
    171	const struct das08_board_struct *board = dev->board_ptr;
    172	struct das08_private_struct *devpriv = dev->private;
    173	int n;
    174	int chan;
    175	int range;
    176	int lsb, msb;
    177	int ret;
    178
    179	chan = CR_CHAN(insn->chanspec);
    180	range = CR_RANGE(insn->chanspec);
    181
    182	/* clear crap */
    183	inb(dev->iobase + DAS08_AI_LSB_REG);
    184	inb(dev->iobase + DAS08_AI_MSB_REG);
    185
    186	/* set multiplexer */
    187	/* lock to prevent race with digital output */
    188	spin_lock(&dev->spinlock);
    189	devpriv->do_mux_bits &= ~DAS08_CONTROL_MUX_MASK;
    190	devpriv->do_mux_bits |= DAS08_CONTROL_MUX(chan);
    191	outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL_REG);
    192	spin_unlock(&dev->spinlock);
    193
    194	if (devpriv->pg_gainlist) {
    195		/* set gain/range */
    196		range = CR_RANGE(insn->chanspec);
    197		outb(devpriv->pg_gainlist[range],
    198		     dev->iobase + DAS08_GAIN_REG);
    199	}
    200
    201	for (n = 0; n < insn->n; n++) {
    202		/* clear over-range bits for 16-bit boards */
    203		if (board->ai_nbits == 16)
    204			if (inb(dev->iobase + DAS08_AI_MSB_REG) & 0x80)
    205				dev_info(dev->class_dev, "over-range\n");
    206
    207		/* trigger conversion */
    208		outb_p(0, dev->iobase + DAS08_AI_TRIG_REG);
    209
    210		ret = comedi_timeout(dev, s, insn, das08_ai_eoc, 0);
    211		if (ret)
    212			return ret;
    213
    214		msb = inb(dev->iobase + DAS08_AI_MSB_REG);
    215		lsb = inb(dev->iobase + DAS08_AI_LSB_REG);
    216		if (board->ai_encoding == das08_encode12) {
    217			data[n] = (lsb >> 4) | (msb << 4);
    218		} else if (board->ai_encoding == das08_pcm_encode12) {
    219			data[n] = (msb << 8) + lsb;
    220		} else if (board->ai_encoding == das08_encode16) {
    221			/*
    222			 * "JR" 16-bit boards are sign-magnitude.
    223			 *
    224			 * XXX The manual seems to imply that 0 is full-scale
    225			 * negative and 65535 is full-scale positive, but the
    226			 * original COMEDI patch to add support for the
    227			 * DAS08/JR/16 and DAS08/JR/16-AO boards have it
    228			 * encoded as sign-magnitude.  Assume the original
    229			 * COMEDI code is correct for now.
    230			 */
    231			unsigned int magnitude = lsb | ((msb & 0x7f) << 8);
    232
    233			/*
    234			 * MSB bit 7 is 0 for negative, 1 for positive voltage.
    235			 * COMEDI 16-bit bipolar data value for 0V is 0x8000.
    236			 */
    237			if (msb & 0x80)
    238				data[n] = BIT(15) + magnitude;
    239			else
    240				data[n] = BIT(15) - magnitude;
    241		} else {
    242			dev_err(dev->class_dev, "bug! unknown ai encoding\n");
    243			return -1;
    244		}
    245	}
    246
    247	return n;
    248}
    249
    250static int das08_di_insn_bits(struct comedi_device *dev,
    251			      struct comedi_subdevice *s,
    252			      struct comedi_insn *insn, unsigned int *data)
    253{
    254	data[0] = 0;
    255	data[1] = DAS08_STATUS_DI(inb(dev->iobase + DAS08_STATUS_REG));
    256
    257	return insn->n;
    258}
    259
    260static int das08_do_insn_bits(struct comedi_device *dev,
    261			      struct comedi_subdevice *s,
    262			      struct comedi_insn *insn, unsigned int *data)
    263{
    264	struct das08_private_struct *devpriv = dev->private;
    265
    266	if (comedi_dio_update_state(s, data)) {
    267		/* prevent race with setting of analog input mux */
    268		spin_lock(&dev->spinlock);
    269		devpriv->do_mux_bits &= ~DAS08_CONTROL_DO_MASK;
    270		devpriv->do_mux_bits |= DAS08_CONTROL_DO(s->state);
    271		outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL_REG);
    272		spin_unlock(&dev->spinlock);
    273	}
    274
    275	data[1] = s->state;
    276
    277	return insn->n;
    278}
    279
    280static int das08jr_di_insn_bits(struct comedi_device *dev,
    281				struct comedi_subdevice *s,
    282				struct comedi_insn *insn, unsigned int *data)
    283{
    284	data[0] = 0;
    285	data[1] = inb(dev->iobase + DAS08JR_DI_REG);
    286
    287	return insn->n;
    288}
    289
    290static int das08jr_do_insn_bits(struct comedi_device *dev,
    291				struct comedi_subdevice *s,
    292				struct comedi_insn *insn, unsigned int *data)
    293{
    294	if (comedi_dio_update_state(s, data))
    295		outb(s->state, dev->iobase + DAS08JR_DO_REG);
    296
    297	data[1] = s->state;
    298
    299	return insn->n;
    300}
    301
    302static void das08_ao_set_data(struct comedi_device *dev,
    303			      unsigned int chan, unsigned int data)
    304{
    305	const struct das08_board_struct *board = dev->board_ptr;
    306	unsigned char lsb;
    307	unsigned char msb;
    308
    309	lsb = data & 0xff;
    310	msb = (data >> 8) & 0xff;
    311	if (board->is_jr) {
    312		outb(lsb, dev->iobase + DAS08JR_AO_LSB_REG(chan));
    313		outb(msb, dev->iobase + DAS08JR_AO_MSB_REG(chan));
    314		/* load DACs */
    315		inb(dev->iobase + DAS08JR_AO_UPDATE_REG);
    316	} else {
    317		outb(lsb, dev->iobase + DAS08AOX_AO_LSB_REG(chan));
    318		outb(msb, dev->iobase + DAS08AOX_AO_MSB_REG(chan));
    319		/* load DACs */
    320		inb(dev->iobase + DAS08AOX_AO_UPDATE_REG);
    321	}
    322}
    323
    324static int das08_ao_insn_write(struct comedi_device *dev,
    325			       struct comedi_subdevice *s,
    326			       struct comedi_insn *insn,
    327			       unsigned int *data)
    328{
    329	unsigned int chan = CR_CHAN(insn->chanspec);
    330	unsigned int val = s->readback[chan];
    331	int i;
    332
    333	for (i = 0; i < insn->n; i++) {
    334		val = data[i];
    335		das08_ao_set_data(dev, chan, val);
    336	}
    337	s->readback[chan] = val;
    338
    339	return insn->n;
    340}
    341
    342int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
    343{
    344	const struct das08_board_struct *board = dev->board_ptr;
    345	struct das08_private_struct *devpriv = dev->private;
    346	struct comedi_subdevice *s;
    347	int ret;
    348	int i;
    349
    350	dev->iobase = iobase;
    351
    352	dev->board_name = board->name;
    353
    354	ret = comedi_alloc_subdevices(dev, 6);
    355	if (ret)
    356		return ret;
    357
    358	s = &dev->subdevices[0];
    359	/* ai */
    360	if (board->ai_nbits) {
    361		s->type = COMEDI_SUBD_AI;
    362		/*
    363		 * XXX some boards actually have differential
    364		 * inputs instead of single ended.
    365		 * The driver does nothing with arefs though,
    366		 * so it's no big deal.
    367		 */
    368		s->subdev_flags = SDF_READABLE | SDF_GROUND;
    369		s->n_chan = 8;
    370		s->maxdata = (1 << board->ai_nbits) - 1;
    371		s->range_table = das08_ai_lranges[board->ai_pg];
    372		s->insn_read = das08_ai_insn_read;
    373		devpriv->pg_gainlist = das08_ai_gainlists[board->ai_pg];
    374	} else {
    375		s->type = COMEDI_SUBD_UNUSED;
    376	}
    377
    378	s = &dev->subdevices[1];
    379	/* ao */
    380	if (board->ao_nbits) {
    381		s->type = COMEDI_SUBD_AO;
    382		s->subdev_flags = SDF_WRITABLE;
    383		s->n_chan = 2;
    384		s->maxdata = (1 << board->ao_nbits) - 1;
    385		s->range_table = &range_bipolar5;
    386		s->insn_write = das08_ao_insn_write;
    387
    388		ret = comedi_alloc_subdev_readback(s);
    389		if (ret)
    390			return ret;
    391
    392		/* initialize all channels to 0V */
    393		for (i = 0; i < s->n_chan; i++) {
    394			s->readback[i] = s->maxdata / 2;
    395			das08_ao_set_data(dev, i, s->readback[i]);
    396		}
    397	} else {
    398		s->type = COMEDI_SUBD_UNUSED;
    399	}
    400
    401	s = &dev->subdevices[2];
    402	/* di */
    403	if (board->di_nchan) {
    404		s->type = COMEDI_SUBD_DI;
    405		s->subdev_flags = SDF_READABLE;
    406		s->n_chan = board->di_nchan;
    407		s->maxdata = 1;
    408		s->range_table = &range_digital;
    409		s->insn_bits = board->is_jr ? das08jr_di_insn_bits :
    410			       das08_di_insn_bits;
    411	} else {
    412		s->type = COMEDI_SUBD_UNUSED;
    413	}
    414
    415	s = &dev->subdevices[3];
    416	/* do */
    417	if (board->do_nchan) {
    418		s->type = COMEDI_SUBD_DO;
    419		s->subdev_flags = SDF_WRITABLE;
    420		s->n_chan = board->do_nchan;
    421		s->maxdata = 1;
    422		s->range_table = &range_digital;
    423		s->insn_bits = board->is_jr ? das08jr_do_insn_bits :
    424			       das08_do_insn_bits;
    425	} else {
    426		s->type = COMEDI_SUBD_UNUSED;
    427	}
    428
    429	s = &dev->subdevices[4];
    430	/* 8255 */
    431	if (board->i8255_offset != 0) {
    432		ret = subdev_8255_init(dev, s, NULL, board->i8255_offset);
    433		if (ret)
    434			return ret;
    435	} else {
    436		s->type = COMEDI_SUBD_UNUSED;
    437	}
    438
    439	/* Counter subdevice (8254) */
    440	s = &dev->subdevices[5];
    441	if (board->i8254_offset) {
    442		dev->pacer = comedi_8254_init(dev->iobase + board->i8254_offset,
    443					      0, I8254_IO8, 0);
    444		if (!dev->pacer)
    445			return -ENOMEM;
    446
    447		comedi_8254_subdevice_init(s, dev->pacer);
    448	} else {
    449		s->type = COMEDI_SUBD_UNUSED;
    450	}
    451
    452	return 0;
    453}
    454EXPORT_SYMBOL_GPL(das08_common_attach);
    455
    456static int __init das08_init(void)
    457{
    458	return 0;
    459}
    460module_init(das08_init);
    461
    462static void __exit das08_exit(void)
    463{
    464}
    465module_exit(das08_exit);
    466
    467MODULE_AUTHOR("Comedi https://www.comedi.org");
    468MODULE_DESCRIPTION("Comedi common DAS08 support module");
    469MODULE_LICENSE("GPL");