mirror of
https://github.com/torvalds/linux.git
synced 2026-02-04 15:28:49 +08:00
Some of the header files in "drivers/comedi/drivers/" are common enough to be useful to out-of-tree comedi driver modules. Using them for out-of-tree module builds is hampered by the headers being outside the "include/" directory so it is desirable to move them. There are about a couple of dozen Comedi device drivers that use the "comedi_8255" module to add digital I/O subdevices based on the venerable 8255 Programmable Peripheral Interface chip. The macros and declarations to use that module are in the "8255.h" header file in the comedi "drivers" directory. Move it into "include/linux/comedi/" and rename it to "comedi_8255.h" for naming consistency reasons. Signed-off-by: Ian Abbott <abbotti@mev.co.uk> Link: https://lore.kernel.org/r/20211117120604.117740-4-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
227 lines
5.5 KiB
C
227 lines
5.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* pcm3724.c
|
|
* Comedi driver for Advantech PCM-3724 Digital I/O board
|
|
*
|
|
* Drew Csillag <drew_csillag@yahoo.com>
|
|
*/
|
|
|
|
/*
|
|
* Driver: pcm3724
|
|
* Description: Advantech PCM-3724
|
|
* Devices: [Advantech] PCM-3724 (pcm3724)
|
|
* Author: Drew Csillag <drew_csillag@yahoo.com>
|
|
* Status: tested
|
|
*
|
|
* This is driver for digital I/O boards PCM-3724 with 48 DIO.
|
|
* It needs 8255.o for operations and only immediate mode is supported.
|
|
* See the source for configuration details.
|
|
*
|
|
* Copy/pasted/hacked from pcm724.c
|
|
*
|
|
* Configuration Options:
|
|
* [0] - I/O port base address
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/comedi/comedidev.h>
|
|
#include <linux/comedi/comedi_8255.h>
|
|
|
|
/*
|
|
* Register I/O Map
|
|
*
|
|
* This board has two standard 8255 devices that provide six 8-bit DIO ports
|
|
* (48 channels total). Six 74HCT245 chips (one for each port) buffer the
|
|
* I/O lines to increase driving capability. Because the 74HCT245 is a
|
|
* bidirectional, tri-state line buffer, two additional I/O ports are used
|
|
* to control the direction of data and the enable of each port.
|
|
*/
|
|
#define PCM3724_8255_0_BASE 0x00
|
|
#define PCM3724_8255_1_BASE 0x04
|
|
#define PCM3724_DIO_DIR_REG 0x08
|
|
#define PCM3724_DIO_DIR_C0_OUT BIT(0)
|
|
#define PCM3724_DIO_DIR_B0_OUT BIT(1)
|
|
#define PCM3724_DIO_DIR_A0_OUT BIT(2)
|
|
#define PCM3724_DIO_DIR_C1_OUT BIT(3)
|
|
#define PCM3724_DIO_DIR_B1_OUT BIT(4)
|
|
#define PCM3724_DIO_DIR_A1_OUT BIT(5)
|
|
#define PCM3724_GATE_CTRL_REG 0x09
|
|
#define PCM3724_GATE_CTRL_C0_ENA BIT(0)
|
|
#define PCM3724_GATE_CTRL_B0_ENA BIT(1)
|
|
#define PCM3724_GATE_CTRL_A0_ENA BIT(2)
|
|
#define PCM3724_GATE_CTRL_C1_ENA BIT(3)
|
|
#define PCM3724_GATE_CTRL_B1_ENA BIT(4)
|
|
#define PCM3724_GATE_CTRL_A1_ENA BIT(5)
|
|
|
|
/* used to track configured dios */
|
|
struct priv_pcm3724 {
|
|
int dio_1;
|
|
int dio_2;
|
|
};
|
|
|
|
static int compute_buffer(int config, int devno, struct comedi_subdevice *s)
|
|
{
|
|
/* 1 in io_bits indicates output */
|
|
if (s->io_bits & 0x0000ff) {
|
|
if (devno == 0)
|
|
config |= PCM3724_DIO_DIR_A0_OUT;
|
|
else
|
|
config |= PCM3724_DIO_DIR_A1_OUT;
|
|
}
|
|
if (s->io_bits & 0x00ff00) {
|
|
if (devno == 0)
|
|
config |= PCM3724_DIO_DIR_B0_OUT;
|
|
else
|
|
config |= PCM3724_DIO_DIR_B1_OUT;
|
|
}
|
|
if (s->io_bits & 0xff0000) {
|
|
if (devno == 0)
|
|
config |= PCM3724_DIO_DIR_C0_OUT;
|
|
else
|
|
config |= PCM3724_DIO_DIR_C1_OUT;
|
|
}
|
|
return config;
|
|
}
|
|
|
|
static void do_3724_config(struct comedi_device *dev,
|
|
struct comedi_subdevice *s, int chanspec)
|
|
{
|
|
struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
|
|
struct comedi_subdevice *s_dio2 = &dev->subdevices[1];
|
|
int config;
|
|
int buffer_config;
|
|
unsigned long port_8255_cfg;
|
|
|
|
config = I8255_CTRL_CW;
|
|
buffer_config = 0;
|
|
|
|
/* 1 in io_bits indicates output, 1 in config indicates input */
|
|
if (!(s->io_bits & 0x0000ff))
|
|
config |= I8255_CTRL_A_IO;
|
|
|
|
if (!(s->io_bits & 0x00ff00))
|
|
config |= I8255_CTRL_B_IO;
|
|
|
|
if (!(s->io_bits & 0xff0000))
|
|
config |= I8255_CTRL_C_HI_IO | I8255_CTRL_C_LO_IO;
|
|
|
|
buffer_config = compute_buffer(0, 0, s_dio1);
|
|
buffer_config = compute_buffer(buffer_config, 1, s_dio2);
|
|
|
|
if (s == s_dio1)
|
|
port_8255_cfg = dev->iobase + I8255_CTRL_REG;
|
|
else
|
|
port_8255_cfg = dev->iobase + I8255_SIZE + I8255_CTRL_REG;
|
|
|
|
outb(buffer_config, dev->iobase + PCM3724_DIO_DIR_REG);
|
|
|
|
outb(config, port_8255_cfg);
|
|
}
|
|
|
|
static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s,
|
|
int chanspec)
|
|
{
|
|
struct priv_pcm3724 *priv = dev->private;
|
|
struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
|
|
unsigned int mask;
|
|
int gatecfg;
|
|
|
|
gatecfg = 0;
|
|
|
|
mask = 1 << CR_CHAN(chanspec);
|
|
if (s == s_dio1)
|
|
priv->dio_1 |= mask;
|
|
else
|
|
priv->dio_2 |= mask;
|
|
|
|
if (priv->dio_1 & 0xff0000)
|
|
gatecfg |= PCM3724_GATE_CTRL_C0_ENA;
|
|
|
|
if (priv->dio_1 & 0xff00)
|
|
gatecfg |= PCM3724_GATE_CTRL_B0_ENA;
|
|
|
|
if (priv->dio_1 & 0xff)
|
|
gatecfg |= PCM3724_GATE_CTRL_A0_ENA;
|
|
|
|
if (priv->dio_2 & 0xff0000)
|
|
gatecfg |= PCM3724_GATE_CTRL_C1_ENA;
|
|
|
|
if (priv->dio_2 & 0xff00)
|
|
gatecfg |= PCM3724_GATE_CTRL_B1_ENA;
|
|
|
|
if (priv->dio_2 & 0xff)
|
|
gatecfg |= PCM3724_GATE_CTRL_A1_ENA;
|
|
|
|
outb(gatecfg, dev->iobase + PCM3724_GATE_CTRL_REG);
|
|
}
|
|
|
|
/* overriding the 8255 insn config */
|
|
static int subdev_3724_insn_config(struct comedi_device *dev,
|
|
struct comedi_subdevice *s,
|
|
struct comedi_insn *insn,
|
|
unsigned int *data)
|
|
{
|
|
unsigned int chan = CR_CHAN(insn->chanspec);
|
|
unsigned int mask;
|
|
int ret;
|
|
|
|
if (chan < 8)
|
|
mask = 0x0000ff;
|
|
else if (chan < 16)
|
|
mask = 0x00ff00;
|
|
else if (chan < 20)
|
|
mask = 0x0f0000;
|
|
else
|
|
mask = 0xf00000;
|
|
|
|
ret = comedi_dio_insn_config(dev, s, insn, data, mask);
|
|
if (ret)
|
|
return ret;
|
|
|
|
do_3724_config(dev, s, insn->chanspec);
|
|
enable_chan(dev, s, insn->chanspec);
|
|
|
|
return insn->n;
|
|
}
|
|
|
|
static int pcm3724_attach(struct comedi_device *dev,
|
|
struct comedi_devconfig *it)
|
|
{
|
|
struct priv_pcm3724 *priv;
|
|
struct comedi_subdevice *s;
|
|
int ret, i;
|
|
|
|
priv = comedi_alloc_devpriv(dev, sizeof(*priv));
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
ret = comedi_request_region(dev, it->options[0], 0x10);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = comedi_alloc_subdevices(dev, 2);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for (i = 0; i < dev->n_subdevices; i++) {
|
|
s = &dev->subdevices[i];
|
|
ret = subdev_8255_init(dev, s, NULL, i * I8255_SIZE);
|
|
if (ret)
|
|
return ret;
|
|
s->insn_config = subdev_3724_insn_config;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct comedi_driver pcm3724_driver = {
|
|
.driver_name = "pcm3724",
|
|
.module = THIS_MODULE,
|
|
.attach = pcm3724_attach,
|
|
.detach = comedi_legacy_detach,
|
|
};
|
|
module_comedi_driver(pcm3724_driver);
|
|
|
|
MODULE_AUTHOR("Comedi https://www.comedi.org");
|
|
MODULE_DESCRIPTION("Comedi driver for Advantech PCM-3724 Digital I/O board");
|
|
MODULE_LICENSE("GPL");
|