162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fl512.c
462306a36Sopenharmony_ci * Anders Gnistrup <ex18@kalman.iau.dtu.dk>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * COMEDI - Linux Control and Measurement Device Interface
762306a36Sopenharmony_ci * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * Driver: fl512
1262306a36Sopenharmony_ci * Description: unknown
1362306a36Sopenharmony_ci * Author: Anders Gnistrup <ex18@kalman.iau.dtu.dk>
1462306a36Sopenharmony_ci * Devices: [unknown] FL512 (fl512)
1562306a36Sopenharmony_ci * Status: unknown
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * Digital I/O is not supported.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Configuration options:
2062306a36Sopenharmony_ci *   [0] - I/O port base address
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <linux/comedi/comedidev.h>
2562306a36Sopenharmony_ci#include <linux/delay.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * Register I/O map
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci#define FL512_AI_LSB_REG		0x02
3162306a36Sopenharmony_ci#define FL512_AI_MSB_REG		0x03
3262306a36Sopenharmony_ci#define FL512_AI_MUX_REG		0x02
3362306a36Sopenharmony_ci#define FL512_AI_START_CONV_REG		0x03
3462306a36Sopenharmony_ci#define FL512_AO_DATA_REG(x)		(0x04 + ((x) * 2))
3562306a36Sopenharmony_ci#define FL512_AO_TRIG_REG(x)		(0x04 + ((x) * 2))
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic const struct comedi_lrange range_fl512 = {
3862306a36Sopenharmony_ci	4, {
3962306a36Sopenharmony_ci		BIP_RANGE(0.5),
4062306a36Sopenharmony_ci		BIP_RANGE(1),
4162306a36Sopenharmony_ci		BIP_RANGE(5),
4262306a36Sopenharmony_ci		BIP_RANGE(10),
4362306a36Sopenharmony_ci		UNI_RANGE(1),
4462306a36Sopenharmony_ci		UNI_RANGE(5),
4562306a36Sopenharmony_ci		UNI_RANGE(10)
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int fl512_ai_insn_read(struct comedi_device *dev,
5062306a36Sopenharmony_ci			      struct comedi_subdevice *s,
5162306a36Sopenharmony_ci			      struct comedi_insn *insn,
5262306a36Sopenharmony_ci			      unsigned int *data)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	unsigned int chan = CR_CHAN(insn->chanspec);
5562306a36Sopenharmony_ci	unsigned int val;
5662306a36Sopenharmony_ci	int i;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	outb(chan, dev->iobase + FL512_AI_MUX_REG);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	for (i = 0; i < insn->n; i++) {
6162306a36Sopenharmony_ci		outb(0, dev->iobase + FL512_AI_START_CONV_REG);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		/* XXX should test "done" flag instead of delay */
6462306a36Sopenharmony_ci		usleep_range(30, 100);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		val = inb(dev->iobase + FL512_AI_LSB_REG);
6762306a36Sopenharmony_ci		val |= (inb(dev->iobase + FL512_AI_MSB_REG) << 8);
6862306a36Sopenharmony_ci		val &= s->maxdata;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		data[i] = val;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return insn->n;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int fl512_ao_insn_write(struct comedi_device *dev,
7762306a36Sopenharmony_ci			       struct comedi_subdevice *s,
7862306a36Sopenharmony_ci			       struct comedi_insn *insn,
7962306a36Sopenharmony_ci			       unsigned int *data)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	unsigned int chan = CR_CHAN(insn->chanspec);
8262306a36Sopenharmony_ci	unsigned int val = s->readback[chan];
8362306a36Sopenharmony_ci	int i;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	for (i = 0; i < insn->n; i++) {
8662306a36Sopenharmony_ci		val = data[i];
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		/* write LSB, MSB then trigger conversion */
8962306a36Sopenharmony_ci		outb(val & 0x0ff, dev->iobase + FL512_AO_DATA_REG(chan));
9062306a36Sopenharmony_ci		outb((val >> 8) & 0xf, dev->iobase + FL512_AO_DATA_REG(chan));
9162306a36Sopenharmony_ci		inb(dev->iobase + FL512_AO_TRIG_REG(chan));
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci	s->readback[chan] = val;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return insn->n;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int fl512_attach(struct comedi_device *dev, struct comedi_devconfig *it)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct comedi_subdevice *s;
10162306a36Sopenharmony_ci	int ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	ret = comedi_request_region(dev, it->options[0], 0x10);
10462306a36Sopenharmony_ci	if (ret)
10562306a36Sopenharmony_ci		return ret;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ret = comedi_alloc_subdevices(dev, 2);
10862306a36Sopenharmony_ci	if (ret)
10962306a36Sopenharmony_ci		return ret;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Analog Input subdevice */
11262306a36Sopenharmony_ci	s = &dev->subdevices[0];
11362306a36Sopenharmony_ci	s->type		= COMEDI_SUBD_AI;
11462306a36Sopenharmony_ci	s->subdev_flags	= SDF_READABLE | SDF_GROUND;
11562306a36Sopenharmony_ci	s->n_chan	= 16;
11662306a36Sopenharmony_ci	s->maxdata	= 0x0fff;
11762306a36Sopenharmony_ci	s->range_table	= &range_fl512;
11862306a36Sopenharmony_ci	s->insn_read	= fl512_ai_insn_read;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Analog Output subdevice */
12162306a36Sopenharmony_ci	s = &dev->subdevices[1];
12262306a36Sopenharmony_ci	s->type		= COMEDI_SUBD_AO;
12362306a36Sopenharmony_ci	s->subdev_flags	= SDF_WRITABLE;
12462306a36Sopenharmony_ci	s->n_chan	= 2;
12562306a36Sopenharmony_ci	s->maxdata	= 0x0fff;
12662306a36Sopenharmony_ci	s->range_table	= &range_fl512;
12762306a36Sopenharmony_ci	s->insn_write	= fl512_ao_insn_write;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return comedi_alloc_subdev_readback(s);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct comedi_driver fl512_driver = {
13362306a36Sopenharmony_ci	.driver_name	= "fl512",
13462306a36Sopenharmony_ci	.module		= THIS_MODULE,
13562306a36Sopenharmony_ci	.attach		= fl512_attach,
13662306a36Sopenharmony_ci	.detach		= comedi_legacy_detach,
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_cimodule_comedi_driver(fl512_driver);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciMODULE_AUTHOR("Comedi https://www.comedi.org");
14162306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi low-level driver");
14262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
143