blob: 8b9fa0f9f1f62606eb0d8e618ae0c0f5e0862ba0 [file] [log] [blame]
/*
comedi/drivers/me4000.c
Source code for the Meilhaus ME-4000 board family.
COMEDI - Linux Control and Measurement Device Interface
Copyright (C) 2000 David A. Schleef <ds@schleef.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
Driver: me4000
Description: Meilhaus ME-4000 series boards
Devices: [Meilhaus] ME-4650 (me4000), ME-4670i, ME-4680, ME-4680i, ME-4680is
Author: gg (Guenter Gebhardt <g.gebhardt@meilhaus.com>)
Updated: Mon, 18 Mar 2002 15:34:01 -0800
Status: broken (no support for loading firmware)
Supports:
- Analog Input
- Analog Output
- Digital I/O
- Counter
Configuration Options:
[0] - PCI bus number (optional)
[1] - PCI slot number (optional)
If bus/slot is not specified, the first available PCI
device will be used.
The firmware required by these boards is available in the
comedi_nonfree_firmware tarball available from
http://www.comedi.org. However, the driver's support for
loading the firmware through comedi_config is currently
broken.
*/
#include <linux/interrupt.h>
#include "../comedidev.h"
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include "comedi_pci.h"
#include "me4000.h"
#if 0
/* file removed due to GPL incompatibility */
#include "me4000_fw.h"
#endif
/*=============================================================================
PCI device table.
This is used by modprobe to translate PCI IDs to drivers.
===========================================================================*/
static DEFINE_PCI_DEVICE_TABLE(me4000_pci_table) = {
{
PCI_VENDOR_ID_MEILHAUS, 0x4650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4661, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4662, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4681, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4682, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
PCI_VENDOR_ID_MEILHAUS, 0x4683, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
0}
};
MODULE_DEVICE_TABLE(pci, me4000_pci_table);
static const struct me4000_board me4000_boards[] = {
{"ME-4650", 0x4650, {0, 0}, {16, 0, 0, 0}, {4}, {0}},
{"ME-4660", 0x4660, {0, 0}, {32, 0, 16, 0}, {4}, {3}},
{"ME-4660i", 0x4661, {0, 0}, {32, 0, 16, 0}, {4}, {3}},
{"ME-4660s", 0x4662, {0, 0}, {32, 8, 16, 0}, {4}, {3}},
{"ME-4660is", 0x4663, {0, 0}, {32, 8, 16, 0}, {4}, {3}},
{"ME-4670", 0x4670, {4, 0}, {32, 0, 16, 1}, {4}, {3}},
{"ME-4670i", 0x4671, {4, 0}, {32, 0, 16, 1}, {4}, {3}},
{"ME-4670s", 0x4672, {4, 0}, {32, 8, 16, 1}, {4}, {3}},
{"ME-4670is", 0x4673, {4, 0}, {32, 8, 16, 1}, {4}, {3}},
{"ME-4680", 0x4680, {4, 4}, {32, 0, 16, 1}, {4}, {3}},
{"ME-4680i", 0x4681, {4, 4}, {32, 0, 16, 1}, {4}, {3}},
{"ME-4680s", 0x4682, {4, 4}, {32, 8, 16, 1}, {4}, {3}},
{"ME-4680is", 0x4683, {4, 4}, {32, 8, 16, 1}, {4}, {3}},
{0},
};
#define ME4000_BOARD_VERSIONS (ARRAY_SIZE(me4000_boards) - 1)
/*-----------------------------------------------------------------------------
Comedi function prototypes
---------------------------------------------------------------------------*/
static int me4000_attach(struct comedi_device *dev,
struct comedi_devconfig *it);
static int me4000_detach(struct comedi_device *dev);
static struct comedi_driver driver_me4000 = {
driver_name:"me4000",
module:THIS_MODULE,
attach:me4000_attach,
detach:me4000_detach,
};
/*-----------------------------------------------------------------------------
Meilhaus function prototypes
---------------------------------------------------------------------------*/
static int me4000_probe(struct comedi_device *dev, struct comedi_devconfig *it);
static int get_registers(struct comedi_device *dev, struct pci_dev *pci_dev_p);
static int init_board_info(struct comedi_device *dev,
struct pci_dev *pci_dev_p);
static int init_ao_context(struct comedi_device *dev);
static int init_ai_context(struct comedi_device *dev);
static int init_dio_context(struct comedi_device *dev);
static int init_cnt_context(struct comedi_device *dev);
static int xilinx_download(struct comedi_device *dev);
static int reset_board(struct comedi_device *dev);
static int me4000_dio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
static int me4000_dio_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
static int cnt_reset(struct comedi_device *dev, unsigned int channel);
static int cnt_config(struct comedi_device *dev,
unsigned int channel, unsigned int mode);
static int me4000_cnt_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
static int me4000_cnt_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
static int me4000_cnt_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
static int me4000_ai_insn_read(struct comedi_device *dev,
struct comedi_subdevice *subdevice,
struct comedi_insn *insn, unsigned int *data);
static int me4000_ai_cancel(struct comedi_device *dev,
struct comedi_subdevice *s);
static int ai_check_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd);
static int ai_round_cmd_args(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd,
unsigned int *init_ticks,
unsigned int *scan_ticks,
unsigned int *chan_ticks);
static int ai_prepare(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd,
unsigned int init_ticks,
unsigned int scan_ticks, unsigned int chan_ticks);
static int ai_write_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd);
static irqreturn_t me4000_ai_isr(int irq, void *dev_id);
static int me4000_ai_do_cmd_test(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd);
static int me4000_ai_do_cmd(struct comedi_device *dev,
struct comedi_subdevice *s);
static int me4000_ao_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
static int me4000_ao_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
/*-----------------------------------------------------------------------------
Meilhaus inline functions
---------------------------------------------------------------------------*/
static inline void me4000_outb(struct comedi_device *dev, unsigned char value,
unsigned long port)
{
PORT_PDEBUG("--> 0x%02X port 0x%04lX\n", value, port);
outb(value, port);
}
static inline void me4000_outl(struct comedi_device *dev, unsigned long value,
unsigned long port)
{
PORT_PDEBUG("--> 0x%08lX port 0x%04lX\n", value, port);
outl(value, port);
}
static inline unsigned long me4000_inl(struct comedi_device *dev,
unsigned long port)
{
unsigned long value;
value = inl(port);
PORT_PDEBUG("<-- 0x%08lX port 0x%04lX\n", value, port);
return value;
}
static inline unsigned char me4000_inb(struct comedi_device *dev,
unsigned long port)
{
unsigned char value;
value = inb(port);
PORT_PDEBUG("<-- 0x%08X port 0x%04lX\n", value, port);
return value;
}
static const struct comedi_lrange me4000_ai_range = {
4,
{
UNI_RANGE(2.5),
UNI_RANGE(10),
BIP_RANGE(2.5),
BIP_RANGE(10),
}
};
static const struct comedi_lrange me4000_ao_range = {
1,
{
BIP_RANGE(10),
}
};
static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
struct comedi_subdevice *s;
int result;
CALL_PDEBUG("In me4000_attach()\n");
result = me4000_probe(dev, it);
if (result)
return result;
/*
* Allocate the subdevice structures. alloc_subdevice() is a
* convenient macro defined in comedidev.h. It relies on
* n_subdevices being set correctly.
*/
if (alloc_subdevices(dev, 4) < 0)
return -ENOMEM;
/*=========================================================================
Analog input subdevice
========================================================================*/
s = dev->subdevices + 0;
if (thisboard->ai.count) {
s->type = COMEDI_SUBD_AI;
s->subdev_flags =
SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
s->n_chan = thisboard->ai.count;
s->maxdata = 0xFFFF; /* 16 bit ADC */
s->len_chanlist = ME4000_AI_CHANNEL_LIST_COUNT;
s->range_table = &me4000_ai_range;
s->insn_read = me4000_ai_insn_read;
if (info->irq > 0) {
if (request_irq(info->irq, me4000_ai_isr,
IRQF_SHARED, "ME-4000", dev)) {
printk
("comedi%d: me4000: me4000_attach(): Unable to allocate irq\n",
dev->minor);
} else {
dev->read_subdev = s;
s->subdev_flags |= SDF_CMD_READ;
s->cancel = me4000_ai_cancel;
s->do_cmdtest = me4000_ai_do_cmd_test;
s->do_cmd = me4000_ai_do_cmd;
}
} else {
printk(KERN_WARNING
"comedi%d: me4000: me4000_attach(): No interrupt available\n",
dev->minor);
}
} else {
s->type = COMEDI_SUBD_UNUSED;
}
/*=========================================================================
Analog output subdevice
========================================================================*/
s = dev->subdevices + 1;
if (thisboard->ao.count) {
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE | SDF_COMMON | SDF_GROUND;
s->n_chan = thisboard->ao.count;
s->maxdata = 0xFFFF; /* 16 bit DAC */
s->range_table = &me4000_ao_range;
s->insn_write = me4000_ao_insn_write;
s->insn_read = me4000_ao_insn_read;
} else {
s->type = COMEDI_SUBD_UNUSED;
}
/*=========================================================================
Digital I/O subdevice
========================================================================*/
s = dev->subdevices + 2;
if (thisboard->dio.count) {
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = thisboard->dio.count * 8;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = me4000_dio_insn_bits;
s->insn_config = me4000_dio_insn_config;
} else {
s->type = COMEDI_SUBD_UNUSED;
}
/*
* Check for optoisolated ME-4000 version. If one the first
* port is a fixed output port and the second is a fixed input port.
*/
if (!me4000_inl(dev, info->dio_context.dir_reg)) {
s->io_bits |= 0xFF;
me4000_outl(dev, ME4000_DIO_CTRL_BIT_MODE_0,
info->dio_context.dir_reg);
}
/*=========================================================================
Counter subdevice
========================================================================*/
s = dev->subdevices + 3;
if (thisboard->cnt.count) {
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = thisboard->cnt.count;
s->maxdata = 0xFFFF; /* 16 bit counters */
s->insn_read = me4000_cnt_insn_read;
s->insn_write = me4000_cnt_insn_write;
s->insn_config = me4000_cnt_insn_config;
} else {
s->type = COMEDI_SUBD_UNUSED;
}
return 0;
}
static int me4000_probe(struct comedi_device *dev, struct comedi_devconfig *it)
{
struct pci_dev *pci_device;
int result, i;
struct me4000_board *board;
CALL_PDEBUG("In me4000_probe()\n");
/* Allocate private memory */
if (alloc_private(dev, sizeof(struct me4000_info)) < 0)
return -ENOMEM;
/*
* Probe the device to determine what device in the series it is.
*/
for (pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
pci_device != NULL;
pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_device)) {
if (pci_device->vendor == PCI_VENDOR_ID_MEILHAUS) {
for (i = 0; i < ME4000_BOARD_VERSIONS; i++) {
if (me4000_boards[i].device_id ==
pci_device->device) {
/* Was a particular bus/slot requested? */
if ((it->options[0] != 0)
|| (it->options[1] != 0)) {
/* Are we on the wrong bus/slot? */
if (pci_device->bus->number !=
it->options[0]
||
PCI_SLOT(pci_device->devfn)
!= it->options[1]) {
continue;
}
}
dev->board_ptr = me4000_boards + i;
board =
(struct me4000_board *)
dev->board_ptr;
info->pci_dev_p = pci_device;
goto found;
}
}
}
}
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): No supported board found (req. bus/slot : %d/%d)\n",
dev->minor, it->options[0], it->options[1]);
return -ENODEV;
found:
printk(KERN_INFO
"comedi%d: me4000: me4000_probe(): Found %s at PCI bus %d, slot %d\n",
dev->minor, me4000_boards[i].name, pci_device->bus->number,
PCI_SLOT(pci_device->devfn));
/* Set data in device structure */
dev->board_name = board->name;
/* Enable PCI device and request regions */
result = comedi_pci_enable(pci_device, dev->board_name);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Cannot enable PCI device and request I/O regions\n",
dev->minor);
return result;
}
/* Get the PCI base registers */
result = get_registers(dev, pci_device);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Cannot get registers\n",
dev->minor);
return result;
}
/* Initialize board info */
result = init_board_info(dev, pci_device);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Cannot init baord info\n",
dev->minor);
return result;
}
/* Init analog output context */
result = init_ao_context(dev);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Cannot init ao context\n",
dev->minor);
return result;
}
/* Init analog input context */
result = init_ai_context(dev);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Cannot init ai context\n",
dev->minor);
return result;
}
/* Init digital I/O context */
result = init_dio_context(dev);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Cannot init dio context\n",
dev->minor);
return result;
}
/* Init counter context */
result = init_cnt_context(dev);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Cannot init cnt context\n",
dev->minor);
return result;
}
/* Download the xilinx firmware */
result = xilinx_download(dev);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Can't download firmware\n",
dev->minor);
return result;
}
/* Make a hardware reset */
result = reset_board(dev);
if (result) {
printk(KERN_ERR
"comedi%d: me4000: me4000_probe(): Can't reset board\n",
dev->minor);
return result;
}
return 0;
}
static int get_registers(struct comedi_device *dev, struct pci_dev *pci_dev_p)
{
CALL_PDEBUG("In get_registers()\n");
/*--------------------------- plx regbase ---------------------------------*/
info->plx_regbase = pci_resource_start(pci_dev_p, 1);
if (info->plx_regbase == 0) {
printk(KERN_ERR
"comedi%d: me4000: get_registers(): PCI base address 1 is not available\n",
dev->minor);
return -ENODEV;
}
info->plx_regbase_size = pci_resource_len(pci_dev_p, 1);
/*--------------------------- me4000 regbase ------------------------------*/
info->me4000_regbase = pci_resource_start(pci_dev_p, 2);
if (info->me4000_regbase == 0) {
printk(KERN_ERR
"comedi%d: me4000: get_registers(): PCI base address 2 is not available\n",
dev->minor);
return -ENODEV;
}
info->me4000_regbase_size = pci_resource_len(pci_dev_p, 2);
/*--------------------------- timer regbase ------------------------------*/
info->timer_regbase = pci_resource_start(pci_dev_p, 3);
if (info->timer_regbase == 0) {
printk(KERN_ERR
"comedi%d: me4000: get_registers(): PCI base address 3 is not available\n",
dev->minor);
return -ENODEV;
}
info->timer_regbase_size = pci_resource_len(pci_dev_p, 3);
/*--------------------------- program regbase ------------------------------*/
info->program_regbase = pci_resource_start(pci_dev_p, 5);
if (info->program_regbase == 0) {
printk(KERN_ERR
"comedi%d: me4000: get_registers(): PCI base address 5 is not available\n",
dev->minor);
return -ENODEV;
}
info->program_regbase_size = pci_resource_len(pci_dev_p, 5);
return 0;
}
static int init_board_info(struct comedi_device *dev, struct pci_dev *pci_dev_p)
{
int result;
CALL_PDEBUG("In init_board_info()\n");
/* Init spin locks */
/* spin_lock_init(&info->preload_lock); */
/* spin_lock_init(&info->ai_ctrl_lock); */
/* Get the serial number */
result = pci_read_config_dword(pci_dev_p, 0x2C, &info->serial_no);
if (result != PCIBIOS_SUCCESSFUL)
return result;
/* Get the hardware revision */
result = pci_read_config_byte(pci_dev_p, 0x08, &info->hw_revision);
if (result != PCIBIOS_SUCCESSFUL)
return result;
/* Get the vendor id */
info->vendor_id = pci_dev_p->vendor;
/* Get the device id */
info->device_id = pci_dev_p->device;
/* Get the irq assigned to the board */
info->irq = pci_dev_p->irq;
return 0;
}
static int init_ao_context(struct comedi_device *dev)
{
int i;
CALL_PDEBUG("In init_ao_context()\n");
for (i = 0; i < thisboard->ao.count; i++) {
/* spin_lock_init(&info->ao_context[i].use_lock); */
info->ao_context[i].irq = info->irq;
switch (i) {
case 0:
info->ao_context[i].ctrl_reg =
info->me4000_regbase + ME4000_AO_00_CTRL_REG;
info->ao_context[i].status_reg =
info->me4000_regbase + ME4000_AO_00_STATUS_REG;
info->ao_context[i].fifo_reg =
info->me4000_regbase + ME4000_AO_00_FIFO_REG;
info->ao_context[i].single_reg =
info->me4000_regbase + ME4000_AO_00_SINGLE_REG;
info->ao_context[i].timer_reg =
info->me4000_regbase + ME4000_AO_00_TIMER_REG;
info->ao_context[i].irq_status_reg =
info->me4000_regbase + ME4000_IRQ_STATUS_REG;
info->ao_context[i].preload_reg =
info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
break;
case 1:
info->ao_context[i].ctrl_reg =
info->me4000_regbase + ME4000_AO_01_CTRL_REG;
info->ao_context[i].status_reg =
info->me4000_regbase + ME4000_AO_01_STATUS_REG;
info->ao_context[i].fifo_reg =
info->me4000_regbase + ME4000_AO_01_FIFO_REG;
info->ao_context[i].single_reg =
info->me4000_regbase + ME4000_AO_01_SINGLE_REG;
info->ao_context[i].timer_reg =
info->me4000_regbase + ME4000_AO_01_TIMER_REG;
info->ao_context[i].irq_status_reg =
info->me4000_regbase + ME4000_IRQ_STATUS_REG;
info->ao_context[i].preload_reg =
info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
break;
case 2:
info->ao_context[i].ctrl_reg =
info->me4000_regbase + ME4000_AO_02_CTRL_REG;
info->ao_context[i].status_reg =
info->me4000_regbase + ME4000_AO_02_STATUS_REG;
info->ao_context[i].fifo_reg =
info->me4000_regbase + ME4000_AO_02_FIFO_REG;
info->ao_context[i].single_reg =
info->me4000_regbase + ME4000_AO_02_SINGLE_REG;
info->ao_context[i].timer_reg =
info->me4000_regbase + ME4000_AO_02_TIMER_REG;
info->ao_context[i].irq_status_reg =
info->me4000_regbase + ME4000_IRQ_STATUS_REG;
info->ao_context[i].preload_reg =
info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
break;
case 3:
info->ao_context[i].ctrl_reg =
info->me4000_regbase + ME4000_AO_03_CTRL_REG;
info->ao_context[i].status_reg =
info->me4000_regbase + ME4000_AO_03_STATUS_REG;
info->ao_context[i].fifo_reg =
info->me4000_regbase + ME4000_AO_03_FIFO_REG;
info->ao_context[i].single_reg =
info->me4000_regbase + ME4000_AO_03_SINGLE_REG;
info->ao_context[i].timer_reg =
info->me4000_regbase + ME4000_AO_03_TIMER_REG;
info->ao_context[i].irq_status_reg =
info->me4000_regbase + ME4000_IRQ_STATUS_REG;
info->ao_context[i].preload_reg =
info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
break;
default:
break;
}
}
return 0;
}
static int init_ai_context(struct comedi_device *dev)
{
CALL_PDEBUG("In init_ai_context()\n");
info->ai_context.irq = info->irq;
info->ai_context.ctrl_reg = info->me4000_regbase + ME4000_AI_CTRL_REG;
info->ai_context.status_reg =
info->me4000_regbase + ME4000_AI_STATUS_REG;
info->ai_context.channel_list_reg =
info->me4000_regbase + ME4000_AI_CHANNEL_LIST_REG;
info->ai_context.data_reg = info->me4000_regbase + ME4000_AI_DATA_REG;
info->ai_context.chan_timer_reg =
info->me4000_regbase + ME4000_AI_CHAN_TIMER_REG;
info->ai_context.chan_pre_timer_reg =
info->me4000_regbase + ME4000_AI_CHAN_PRE_TIMER_REG;
info->ai_context.scan_timer_low_reg =
info->me4000_regbase + ME4000_AI_SCAN_TIMER_LOW_REG;
info->ai_context.scan_timer_high_reg =
info->me4000_regbase + ME4000_AI_SCAN_TIMER_HIGH_REG;
info->ai_context.scan_pre_timer_low_reg =
info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG;
info->ai_context.scan_pre_timer_high_reg =
info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG;
info->ai_context.start_reg = info->me4000_regbase + ME4000_AI_START_REG;
info->ai_context.irq_status_reg =
info->me4000_regbase + ME4000_IRQ_STATUS_REG;
info->ai_context.sample_counter_reg =
info->me4000_regbase + ME4000_AI_SAMPLE_COUNTER_REG;
return 0;
}
static int init_dio_context(struct comedi_device *dev)
{
CALL_PDEBUG("In init_dio_context()\n");
info->dio_context.dir_reg = info->me4000_regbase + ME4000_DIO_DIR_REG;
info->dio_context.ctrl_reg = info->me4000_regbase + ME4000_DIO_CTRL_REG;
info->dio_context.port_0_reg =
info->me4000_regbase + ME4000_DIO_PORT_0_REG;
info->dio_context.port_1_reg =
info->me4000_regbase + ME4000_DIO_PORT_1_REG;
info->dio_context.port_2_reg =
info->me4000_regbase + ME4000_DIO_PORT_2_REG;
info->dio_context.port_3_reg =
info->me4000_regbase + ME4000_DIO_PORT_3_REG;
return 0;
}
static int init_cnt_context(struct comedi_device *dev)
{
CALL_PDEBUG("In init_cnt_context()\n");
info->cnt_context.ctrl_reg = info->timer_regbase + ME4000_CNT_CTRL_REG;
info->cnt_context.counter_0_reg =
info->timer_regbase + ME4000_CNT_COUNTER_0_REG;
info->cnt_context.counter_1_reg =
info->timer_regbase + ME4000_CNT_COUNTER_1_REG;
info->cnt_context.counter_2_reg =
info->timer_regbase + ME4000_CNT_COUNTER_2_REG;
return 0;
}
#define FIRMWARE_NOT_AVAILABLE 1
#if FIRMWARE_NOT_AVAILABLE
extern unsigned char *xilinx_firm;
#endif
static int xilinx_download(struct comedi_device *dev)
{
u32 value = 0;
wait_queue_head_t queue;
int idx = 0;
int size = 0;
CALL_PDEBUG("In xilinx_download()\n");
init_waitqueue_head(&queue);
/*
* Set PLX local interrupt 2 polarity to high.
* Interrupt is thrown by init pin of xilinx.
*/
outl(0x10, info->plx_regbase + PLX_INTCSR);
/* Set /CS and /WRITE of the Xilinx */
value = inl(info->plx_regbase + PLX_ICR);
value |= 0x100;
outl(value, info->plx_regbase + PLX_ICR);
/* Init Xilinx with CS1 */
inb(info->program_regbase + 0xC8);
/* Wait until /INIT pin is set */
udelay(20);
if (!(inl(info->plx_regbase + PLX_INTCSR) & 0x20)) {
printk(KERN_ERR
"comedi%d: me4000: xilinx_download(): Can't init Xilinx\n",
dev->minor);
return -EIO;
}
/* Reset /CS and /WRITE of the Xilinx */
value = inl(info->plx_regbase + PLX_ICR);
value &= ~0x100;
outl(value, info->plx_regbase + PLX_ICR);
if (FIRMWARE_NOT_AVAILABLE) {
comedi_error(dev,
"xilinx firmware unavailable due to licensing, aborting");
return -EIO;
} else {
/* Download Xilinx firmware */
size = (xilinx_firm[0] << 24) + (xilinx_firm[1] << 16) +
(xilinx_firm[2] << 8) + xilinx_firm[3];
udelay(10);
for (idx = 0; idx < size; idx++) {
outb(xilinx_firm[16 + idx], info->program_regbase);
udelay(10);
/* Check if BUSY flag is low */
if (inl(info->plx_regbase + PLX_ICR) & 0x20) {
printk(KERN_ERR
"comedi%d: me4000: xilinx_download(): Xilinx is still busy (idx = %d)\n",
dev->minor, idx);
return -EIO;
}
}
}
/* If done flag is high download was successful */
if (inl(info->plx_regbase + PLX_ICR) & 0x4) {
} else {
printk(KERN_ERR
"comedi%d: me4000: xilinx_download(): DONE flag is not set\n",
dev->minor);
printk(KERN_ERR
"comedi%d: me4000: xilinx_download(): Download not successful\n",
dev->minor);
return -EIO;
}
/* Set /CS and /WRITE */
value = inl(info->plx_regbase + PLX_ICR);
value |= 0x100;
outl(value, info->plx_regbase + PLX_ICR);
return 0;
}
static int reset_board(struct comedi_device *dev)
{
unsigned long icr;
CALL_PDEBUG("In reset_board()\n");
/* Make a hardware reset */
icr = me4000_inl(dev, info->plx_regbase + PLX_ICR);
icr |= 0x40000000;
me4000_outl(dev, icr, info->plx_regbase + PLX_ICR);
icr &= ~0x40000000;
me4000_outl(dev, icr, info->plx_regbase + PLX_ICR);
/* 0x8000 to the DACs means an output voltage of 0V */
me4000_outl(dev, 0x8000,
info->me4000_regbase + ME4000_AO_00_SINGLE_REG);
me4000_outl(dev, 0x8000,
info->me4000_regbase + ME4000_AO_01_SINGLE_REG);
me4000_outl(dev, 0x8000,
info->me4000_regbase + ME4000_AO_02_SINGLE_REG);
me4000_outl(dev, 0x8000,
info->me4000_regbase + ME4000_AO_03_SINGLE_REG);
/* Set both stop bits in the analog input control register */
me4000_outl(dev,
ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP,
info->me4000_regbase + ME4000_AI_CTRL_REG);
/* Set both stop bits in the analog output control register */
me4000_outl(dev,
ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
info->me4000_regbase + ME4000_AO_00_CTRL_REG);
me4000_outl(dev,
ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
info->me4000_regbase + ME4000_AO_01_CTRL_REG);
me4000_outl(dev,
ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
info->me4000_regbase + ME4000_AO_02_CTRL_REG);
me4000_outl(dev,
ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
info->me4000_regbase + ME4000_AO_03_CTRL_REG);
/* Enable interrupts on the PLX */
me4000_outl(dev, 0x43, info->plx_regbase + PLX_INTCSR);
/* Set the adustment register for AO demux */
me4000_outl(dev, ME4000_AO_DEMUX_ADJUST_VALUE,
info->me4000_regbase + ME4000_AO_DEMUX_ADJUST_REG);
/* Set digital I/O direction for port 0 to output on isolated versions */
if (!(me4000_inl(dev, info->me4000_regbase + ME4000_DIO_DIR_REG) & 0x1)) {
me4000_outl(dev, 0x1,
info->me4000_regbase + ME4000_DIO_CTRL_REG);
}
return 0;
}
static int me4000_detach(struct comedi_device *dev)
{
CALL_PDEBUG("In me4000_detach()\n");
if (info) {
if (info->pci_dev_p) {
reset_board(dev);
if (info->plx_regbase)
comedi_pci_disable(info->pci_dev_p);
pci_dev_put(info->pci_dev_p);
}
}
return 0;
}
/*=============================================================================
Analog input section
===========================================================================*/
static int me4000_ai_insn_read(struct comedi_device *dev,
struct comedi_subdevice *subdevice,
struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
int rang = CR_RANGE(insn->chanspec);
int aref = CR_AREF(insn->chanspec);
unsigned long entry = 0;
unsigned long tmp;
long lval;
CALL_PDEBUG("In me4000_ai_insn_read()\n");
if (insn->n == 0) {
return 0;
} else if (insn->n > 1) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_insn_read(): Invalid instruction length %d\n",
dev->minor, insn->n);
return -EINVAL;
}
switch (rang) {
case 0:
entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5;
break;
case 1:
entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10;
break;
case 2:
entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5;
break;
case 3:
entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_insn_read(): Invalid range specified\n",
dev->minor);
return -EINVAL;
}
switch (aref) {
case AREF_GROUND:
case AREF_COMMON:
if (chan >= thisboard->ai.count) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_insn_read(): Analog input is not available\n",
dev->minor);
return -EINVAL;
}
entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED | chan;
break;
case AREF_DIFF:
if (rang == 0 || rang == 1) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_insn_read(): Range must be bipolar when aref = diff\n",
dev->minor);
return -EINVAL;
}
if (chan >= thisboard->ai.diff_count) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_insn_read(): Analog input is not available\n",
dev->minor);
return -EINVAL;
}
entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL | chan;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_insn_read(): Invalid aref specified\n",
dev->minor);
return -EINVAL;
}
entry |= ME4000_AI_LIST_LAST_ENTRY;
/* Clear channel list, data fifo and both stop bits */
tmp = me4000_inl(dev, info->ai_context.ctrl_reg);
tmp &= ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
ME4000_AI_CTRL_BIT_DATA_FIFO |
ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
me4000_outl(dev, tmp, info->ai_context.ctrl_reg);
/* Set the acquisition mode to single */
tmp &= ~(ME4000_AI_CTRL_BIT_MODE_0 | ME4000_AI_CTRL_BIT_MODE_1 |
ME4000_AI_CTRL_BIT_MODE_2);
me4000_outl(dev, tmp, info->ai_context.ctrl_reg);
/* Enable channel list and data fifo */
tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO;
me4000_outl(dev, tmp, info->ai_context.ctrl_reg);
/* Generate channel list entry */
me4000_outl(dev, entry, info->ai_context.channel_list_reg);
/* Set the timer to maximum sample rate */
me4000_outl(dev, ME4000_AI_MIN_TICKS, info->ai_context.chan_timer_reg);
me4000_outl(dev, ME4000_AI_MIN_TICKS,
info->ai_context.chan_pre_timer_reg);
/* Start conversion by dummy read */
me4000_inl(dev, info->ai_context.start_reg);
/* Wait until ready */
udelay(10);
if (!
(me4000_inl(dev, info->ai_context.status_reg) &
ME4000_AI_STATUS_BIT_EF_DATA)) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_insn_read(): Value not available after wait\n",
dev->minor);
return -EIO;
}
/* Read value from data fifo */
lval = me4000_inl(dev, info->ai_context.data_reg) & 0xFFFF;
data[0] = lval ^ 0x8000;
return 1;
}
static int me4000_ai_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
unsigned long tmp;
CALL_PDEBUG("In me4000_ai_cancel()\n");
/* Stop any running conversion */
tmp = me4000_inl(dev, info->ai_context.ctrl_reg);
tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
me4000_outl(dev, tmp, info->ai_context.ctrl_reg);
/* Clear the control register */
me4000_outl(dev, 0x0, info->ai_context.ctrl_reg);
return 0;
}
static int ai_check_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
int aref;
int i;
CALL_PDEBUG("In ai_check_chanlist()\n");
/* Check whether a channel list is available */
if (!cmd->chanlist_len) {
printk(KERN_ERR
"comedi%d: me4000: ai_check_chanlist(): No channel list available\n",
dev->minor);
return -EINVAL;
}
/* Check the channel list size */
if (cmd->chanlist_len > ME4000_AI_CHANNEL_LIST_COUNT) {
printk(KERN_ERR
"comedi%d: me4000: ai_check_chanlist(): Channel list is to large\n",
dev->minor);
return -EINVAL;
}
/* Check the pointer */
if (!cmd->chanlist) {
printk(KERN_ERR
"comedi%d: me4000: ai_check_chanlist(): NULL pointer to channel list\n",
dev->minor);
return -EFAULT;
}
/* Check whether aref is equal for all entries */
aref = CR_AREF(cmd->chanlist[0]);
for (i = 0; i < cmd->chanlist_len; i++) {
if (CR_AREF(cmd->chanlist[i]) != aref) {
printk(KERN_ERR
"comedi%d: me4000: ai_check_chanlist(): Mode is not equal for all entries\n",
dev->minor);
return -EINVAL;
}
}
/* Check whether channels are available for this ending */
if (aref == SDF_DIFF) {
for (i = 0; i < cmd->chanlist_len; i++) {
if (CR_CHAN(cmd->chanlist[i]) >=
thisboard->ai.diff_count) {
printk(KERN_ERR
"comedi%d: me4000: ai_check_chanlist(): Channel number to high\n",
dev->minor);
return -EINVAL;
}
}
} else {
for (i = 0; i < cmd->chanlist_len; i++) {
if (CR_CHAN(cmd->chanlist[i]) >= thisboard->ai.count) {
printk(KERN_ERR
"comedi%d: me4000: ai_check_chanlist(): Channel number to high\n",
dev->minor);
return -EINVAL;
}
}
}
/* Check if bipolar is set for all entries when in differential mode */
if (aref == SDF_DIFF) {
for (i = 0; i < cmd->chanlist_len; i++) {
if (CR_RANGE(cmd->chanlist[i]) != 1 &&
CR_RANGE(cmd->chanlist[i]) != 2) {
printk(KERN_ERR
"comedi%d: me4000: ai_check_chanlist(): Bipolar is not selected in differential mode\n",
dev->minor);
return -EINVAL;
}
}
}
return 0;
}
static int ai_round_cmd_args(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd,
unsigned int *init_ticks,
unsigned int *scan_ticks, unsigned int *chan_ticks)
{
int rest;
CALL_PDEBUG("In ai_round_cmd_args()\n");
*init_ticks = 0;
*scan_ticks = 0;
*chan_ticks = 0;
PDEBUG("ai_round_cmd_arg(): start_arg = %d\n", cmd->start_arg);
PDEBUG("ai_round_cmd_arg(): scan_begin_arg = %d\n",
cmd->scan_begin_arg);
PDEBUG("ai_round_cmd_arg(): convert_arg = %d\n", cmd->convert_arg);
if (cmd->start_arg) {
*init_ticks = (cmd->start_arg * 33) / 1000;
rest = (cmd->start_arg * 33) % 1000;
if (cmd->flags & TRIG_ROUND_NEAREST) {
if (rest > 33)
(*init_ticks)++;
} else if (cmd->flags & TRIG_ROUND_UP) {
if (rest)
(*init_ticks)++;
}
}
if (cmd->scan_begin_arg) {
*scan_ticks = (cmd->scan_begin_arg * 33) / 1000;
rest = (cmd->scan_begin_arg * 33) % 1000;
if (cmd->flags & TRIG_ROUND_NEAREST) {
if (rest > 33)
(*scan_ticks)++;
} else if (cmd->flags & TRIG_ROUND_UP) {
if (rest)
(*scan_ticks)++;
}
}
if (cmd->convert_arg) {
*chan_ticks = (cmd->convert_arg * 33) / 1000;
rest = (cmd->convert_arg * 33) % 1000;
if (cmd->flags & TRIG_ROUND_NEAREST) {
if (rest > 33)
(*chan_ticks)++;
} else if (cmd->flags & TRIG_ROUND_UP) {
if (rest)
(*chan_ticks)++;
}
}
PDEBUG("ai_round_cmd_args(): init_ticks = %d\n", *init_ticks);
PDEBUG("ai_round_cmd_args(): scan_ticks = %d\n", *scan_ticks);
PDEBUG("ai_round_cmd_args(): chan_ticks = %d\n", *chan_ticks);
return 0;
}
static void ai_write_timer(struct comedi_device *dev,
unsigned int init_ticks,
unsigned int scan_ticks, unsigned int chan_ticks)
{
CALL_PDEBUG("In ai_write_timer()\n");
me4000_outl(dev, init_ticks - 1,
info->ai_context.scan_pre_timer_low_reg);
me4000_outl(dev, 0x0, info->ai_context.scan_pre_timer_high_reg);
if (scan_ticks) {
me4000_outl(dev, scan_ticks - 1,
info->ai_context.scan_timer_low_reg);
me4000_outl(dev, 0x0, info->ai_context.scan_timer_high_reg);
}
me4000_outl(dev, chan_ticks - 1, info->ai_context.chan_pre_timer_reg);
me4000_outl(dev, chan_ticks - 1, info->ai_context.chan_timer_reg);
}
static int ai_prepare(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd,
unsigned int init_ticks,
unsigned int scan_ticks, unsigned int chan_ticks)
{
unsigned long tmp = 0;
CALL_PDEBUG("In ai_prepare()\n");
/* Write timer arguments */
ai_write_timer(dev, init_ticks, scan_ticks, chan_ticks);
/* Reset control register */
me4000_outl(dev, tmp, info->ai_context.ctrl_reg);
/* Start sources */
if ((cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_TIMER &&
cmd->convert_src == TRIG_TIMER) ||
(cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_FOLLOW &&
cmd->convert_src == TRIG_TIMER)) {
tmp = ME4000_AI_CTRL_BIT_MODE_1 |
ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
ME4000_AI_CTRL_BIT_DATA_FIFO;
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_EXT &&
cmd->convert_src == TRIG_TIMER) {
tmp = ME4000_AI_CTRL_BIT_MODE_2 |
ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
ME4000_AI_CTRL_BIT_DATA_FIFO;
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_EXT &&
cmd->convert_src == TRIG_EXT) {
tmp = ME4000_AI_CTRL_BIT_MODE_0 |
ME4000_AI_CTRL_BIT_MODE_1 |
ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
ME4000_AI_CTRL_BIT_DATA_FIFO;
} else {
tmp = ME4000_AI_CTRL_BIT_MODE_0 |
ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
ME4000_AI_CTRL_BIT_DATA_FIFO;
}
/* Stop triggers */
if (cmd->stop_src == TRIG_COUNT) {
me4000_outl(dev, cmd->chanlist_len * cmd->stop_arg,
info->ai_context.sample_counter_reg);
tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ;
} else if (cmd->stop_src == TRIG_NONE &&
cmd->scan_end_src == TRIG_COUNT) {
me4000_outl(dev, cmd->scan_end_arg,
info->ai_context.sample_counter_reg);
tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ;
} else {
tmp |= ME4000_AI_CTRL_BIT_HF_IRQ;
}
/* Write the setup to the control register */
me4000_outl(dev, tmp, info->ai_context.ctrl_reg);
/* Write the channel list */
ai_write_chanlist(dev, s, cmd);
return 0;
}
static int ai_write_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
unsigned int entry;
unsigned int chan;
unsigned int rang;
unsigned int aref;
int i;
CALL_PDEBUG("In ai_write_chanlist()\n");
for (i = 0; i < cmd->chanlist_len; i++) {
chan = CR_CHAN(cmd->chanlist[i]);
rang = CR_RANGE(cmd->chanlist[i]);
aref = CR_AREF(cmd->chanlist[i]);
entry = chan;
if (rang == 0) {
entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5;
} else if (rang == 1) {
entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10;
} else if (rang == 2) {
entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5;
} else {
entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
}
if (aref == SDF_DIFF) {
entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL;
} else {
entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED;
}
me4000_outl(dev, entry, info->ai_context.channel_list_reg);
}
return 0;
}
static int me4000_ai_do_cmd(struct comedi_device *dev,
struct comedi_subdevice *s)
{
int err;
unsigned int init_ticks = 0;
unsigned int scan_ticks = 0;
unsigned int chan_ticks = 0;
struct comedi_cmd *cmd = &s->async->cmd;
CALL_PDEBUG("In me4000_ai_do_cmd()\n");
/* Reset the analog input */
err = me4000_ai_cancel(dev, s);
if (err)
return err;
/* Round the timer arguments */
err = ai_round_cmd_args(dev,
s, cmd, &init_ticks, &scan_ticks, &chan_ticks);
if (err)
return err;
/* Prepare the AI for acquisition */
err = ai_prepare(dev, s, cmd, init_ticks, scan_ticks, chan_ticks);
if (err)
return err;
/* Start acquistion by dummy read */
me4000_inl(dev, info->ai_context.start_reg);
return 0;
}
/*
* me4000_ai_do_cmd_test():
*
* The demo cmd.c in ./comedilib/demo specifies 6 return values:
* - success
* - invalid source
* - source conflict
* - invalid argument
* - argument conflict
* - invalid chanlist
* So I tried to adopt this scheme.
*/
static int me4000_ai_do_cmd_test(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
unsigned int init_ticks;
unsigned int chan_ticks;
unsigned int scan_ticks;
int err = 0;
CALL_PDEBUG("In me4000_ai_do_cmd_test()\n");
PDEBUG("me4000_ai_do_cmd_test(): subdev = %d\n", cmd->subdev);
PDEBUG("me4000_ai_do_cmd_test(): flags = %08X\n", cmd->flags);
PDEBUG("me4000_ai_do_cmd_test(): start_src = %08X\n",
cmd->start_src);
PDEBUG("me4000_ai_do_cmd_test(): start_arg = %d\n",
cmd->start_arg);
PDEBUG("me4000_ai_do_cmd_test(): scan_begin_src = %08X\n",
cmd->scan_begin_src);
PDEBUG("me4000_ai_do_cmd_test(): scan_begin_arg = %d\n",
cmd->scan_begin_arg);
PDEBUG("me4000_ai_do_cmd_test(): convert_src = %08X\n",
cmd->convert_src);
PDEBUG("me4000_ai_do_cmd_test(): convert_arg = %d\n",
cmd->convert_arg);
PDEBUG("me4000_ai_do_cmd_test(): scan_end_src = %08X\n",
cmd->scan_end_src);
PDEBUG("me4000_ai_do_cmd_test(): scan_end_arg = %d\n",
cmd->scan_end_arg);
PDEBUG("me4000_ai_do_cmd_test(): stop_src = %08X\n",
cmd->stop_src);
PDEBUG("me4000_ai_do_cmd_test(): stop_arg = %d\n", cmd->stop_arg);
PDEBUG("me4000_ai_do_cmd_test(): chanlist = %d\n",
(unsigned int)cmd->chanlist);
PDEBUG("me4000_ai_do_cmd_test(): chanlist_len = %d\n",
cmd->chanlist_len);
/* Only rounding flags are implemented */
cmd->flags &= TRIG_ROUND_NEAREST | TRIG_ROUND_UP | TRIG_ROUND_DOWN;
/* Round the timer arguments */
ai_round_cmd_args(dev, s, cmd, &init_ticks, &scan_ticks, &chan_ticks);
/*
* Stage 1. Check if the trigger sources are generally valid.
*/
switch (cmd->start_src) {
case TRIG_NOW:
case TRIG_EXT:
break;
case TRIG_ANY:
cmd->start_src &= TRIG_NOW | TRIG_EXT;
err++;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start source\n",
dev->minor);
cmd->start_src = TRIG_NOW;
err++;
}
switch (cmd->scan_begin_src) {
case TRIG_FOLLOW:
case TRIG_TIMER:
case TRIG_EXT:
break;
case TRIG_ANY:
cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT;
err++;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan begin source\n",
dev->minor);
cmd->scan_begin_src = TRIG_FOLLOW;
err++;
}
switch (cmd->convert_src) {
case TRIG_TIMER:
case TRIG_EXT:
break;
case TRIG_ANY:
cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
err++;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert source\n",
dev->minor);
cmd->convert_src = TRIG_TIMER;
err++;
}
switch (cmd->scan_end_src) {
case TRIG_NONE:
case TRIG_COUNT:
break;
case TRIG_ANY:
cmd->scan_end_src &= TRIG_NONE | TRIG_COUNT;
err++;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end source\n",
dev->minor);
cmd->scan_end_src = TRIG_NONE;
err++;
}
switch (cmd->stop_src) {
case TRIG_NONE:
case TRIG_COUNT:
break;
case TRIG_ANY:
cmd->stop_src &= TRIG_NONE | TRIG_COUNT;
err++;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid stop source\n",
dev->minor);
cmd->stop_src = TRIG_NONE;
err++;
}
if (err)
return 1;
/*
* Stage 2. Check for trigger source conflicts.
*/
if (cmd->start_src == TRIG_NOW &&
cmd->scan_begin_src == TRIG_TIMER &&
cmd->convert_src == TRIG_TIMER) {
} else if (cmd->start_src == TRIG_NOW &&
cmd->scan_begin_src == TRIG_FOLLOW &&
cmd->convert_src == TRIG_TIMER) {
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_TIMER &&
cmd->convert_src == TRIG_TIMER) {
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_FOLLOW &&
cmd->convert_src == TRIG_TIMER) {
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_EXT &&
cmd->convert_src == TRIG_TIMER) {
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_EXT &&
cmd->convert_src == TRIG_EXT) {
} else {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start trigger combination\n",
dev->minor);
cmd->start_src = TRIG_NOW;
cmd->scan_begin_src = TRIG_FOLLOW;
cmd->convert_src = TRIG_TIMER;
err++;
}
if (cmd->stop_src == TRIG_NONE && cmd->scan_end_src == TRIG_NONE) {
} else if (cmd->stop_src == TRIG_COUNT &&
cmd->scan_end_src == TRIG_NONE) {
} else if (cmd->stop_src == TRIG_NONE &&
cmd->scan_end_src == TRIG_COUNT) {
} else if (cmd->stop_src == TRIG_COUNT &&
cmd->scan_end_src == TRIG_COUNT) {
} else {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid stop trigger combination\n",
dev->minor);
cmd->stop_src = TRIG_NONE;
cmd->scan_end_src = TRIG_NONE;
err++;
}
if (err)
return 2;
/*
* Stage 3. Check if arguments are generally valid.
*/
if (cmd->chanlist_len < 1) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): No channel list\n",
dev->minor);
cmd->chanlist_len = 1;
err++;
}
if (init_ticks < 66) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Start arg to low\n",
dev->minor);
cmd->start_arg = 2000;
err++;
}
if (scan_ticks && scan_ticks < 67) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Scan begin arg to low\n",
dev->minor);
cmd->scan_begin_arg = 2031;
err++;
}
if (chan_ticks < 66) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Convert arg to low\n",
dev->minor);
cmd->convert_arg = 2000;
err++;
}
if (err)
return 3;
/*
* Stage 4. Check for argument conflicts.
*/
if (cmd->start_src == TRIG_NOW &&
cmd->scan_begin_src == TRIG_TIMER &&
cmd->convert_src == TRIG_TIMER) {
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end arg\n",
dev->minor);
cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; /* At least one tick more */
err++;
}
} else if (cmd->start_src == TRIG_NOW &&
cmd->scan_begin_src == TRIG_FOLLOW &&
cmd->convert_src == TRIG_TIMER) {
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_TIMER &&
cmd->convert_src == TRIG_TIMER) {
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end arg\n",
dev->minor);
cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31; /* At least one tick more */
err++;
}
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_FOLLOW &&
cmd->convert_src == TRIG_TIMER) {
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_EXT &&
cmd->convert_src == TRIG_TIMER) {
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid convert arg\n",
dev->minor);
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
} else if (cmd->start_src == TRIG_EXT &&
cmd->scan_begin_src == TRIG_EXT &&
cmd->convert_src == TRIG_EXT) {
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid start arg\n",
dev->minor);
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
}
if (cmd->stop_src == TRIG_COUNT) {
if (cmd->stop_arg == 0) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid stop arg\n",
dev->minor);
cmd->stop_arg = 1;
err++;
}
}
if (cmd->scan_end_src == TRIG_COUNT) {
if (cmd->scan_end_arg == 0) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_do_cmd_test(): Invalid scan end arg\n",
dev->minor);
cmd->scan_end_arg = 1;
err++;
}
}
if (err)
return 4;
/*
* Stage 5. Check the channel list.
*/
if (ai_check_chanlist(dev, s, cmd))
return 5;
return 0;
}
static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
{
unsigned int tmp;
struct comedi_device *dev = dev_id;
struct comedi_subdevice *s = dev->subdevices;
struct me4000_ai_context *ai_context = &info->ai_context;
int i;
int c = 0;
long lval;
ISR_PDEBUG("me4000_ai_isr() is executed\n");
if (!dev->attached) {
ISR_PDEBUG("me4000_ai_isr() premature interrupt\n");
return IRQ_NONE;
}
/* Reset all events */
s->async->events = 0;
/* Check if irq number is right */
if (irq != ai_context->irq) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_isr(): Incorrect interrupt num: %d\n",
dev->minor, irq);
return IRQ_HANDLED;
}
if (me4000_inl(dev,
ai_context->irq_status_reg) &
ME4000_IRQ_STATUS_BIT_AI_HF) {
ISR_PDEBUG
("me4000_ai_isr(): Fifo half full interrupt occured\n");
/* Read status register to find out what happened */
tmp = me4000_inl(dev, ai_context->ctrl_reg);
if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) &&
!(tmp & ME4000_AI_STATUS_BIT_HF_DATA) &&
(tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
ISR_PDEBUG("me4000_ai_isr(): Fifo full\n");
c = ME4000_AI_FIFO_COUNT;
/* FIFO overflow, so stop conversion and disable all interrupts */
tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
ME4000_AI_CTRL_BIT_SC_IRQ);
me4000_outl(dev, tmp, ai_context->ctrl_reg);
s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_isr(): FIFO overflow\n",
dev->minor);
} else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA)
&& !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
&& (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
ISR_PDEBUG("me4000_ai_isr(): Fifo half full\n");
s->async->events |= COMEDI_CB_BLOCK;
c = ME4000_AI_FIFO_COUNT / 2;
} else {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_isr(): Can't determine state of fifo\n",
dev->minor);
c = 0;
/* Undefined state, so stop conversion and disable all interrupts */
tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
ME4000_AI_CTRL_BIT_SC_IRQ);
me4000_outl(dev, tmp, ai_context->ctrl_reg);
s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_isr(): Undefined FIFO state\n",
dev->minor);
}
ISR_PDEBUG("me4000_ai_isr(): Try to read %d values\n", c);
for (i = 0; i < c; i++) {
/* Read value from data fifo */
lval = inl(ai_context->data_reg) & 0xFFFF;
lval ^= 0x8000;
if (!comedi_buf_put(s->async, lval)) {
/* Buffer overflow, so stop conversion and disable all interrupts */
tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
ME4000_AI_CTRL_BIT_SC_IRQ);
me4000_outl(dev, tmp, ai_context->ctrl_reg);
s->async->events |= COMEDI_CB_OVERFLOW;
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_isr(): Buffer overflow\n",
dev->minor);
break;
}
}
/* Work is done, so reset the interrupt */
ISR_PDEBUG("me4000_ai_isr(): Reset fifo half full interrupt\n");
tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
me4000_outl(dev, tmp, ai_context->ctrl_reg);
tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
me4000_outl(dev, tmp, ai_context->ctrl_reg);
}
if (me4000_inl(dev,
ai_context->irq_status_reg) & ME4000_IRQ_STATUS_BIT_SC) {
ISR_PDEBUG
("me4000_ai_isr(): Sample counter interrupt occured\n");
s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOA;
/* Acquisition is complete, so stop conversion and disable all interrupts */
tmp = me4000_inl(dev, ai_context->ctrl_reg);
tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ);
me4000_outl(dev, tmp, ai_context->ctrl_reg);
/* Poll data until fifo empty */
while (inl(ai_context->ctrl_reg) & ME4000_AI_STATUS_BIT_EF_DATA) {
/* Read value from data fifo */
lval = inl(ai_context->data_reg) & 0xFFFF;
lval ^= 0x8000;
if (!comedi_buf_put(s->async, lval)) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ai_isr(): Buffer overflow\n",
dev->minor);
s->async->events |= COMEDI_CB_OVERFLOW;
break;
}
}
/* Work is done, so reset the interrupt */
ISR_PDEBUG
("me4000_ai_isr(): Reset interrupt from sample counter\n");
tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
me4000_outl(dev, tmp, ai_context->ctrl_reg);
tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
me4000_outl(dev, tmp, ai_context->ctrl_reg);
}
ISR_PDEBUG("me4000_ai_isr(): Events = 0x%X\n", s->async->events);
if (s->async->events)
comedi_event(dev, s);
return IRQ_HANDLED;
}
/*=============================================================================
Analog output section
===========================================================================*/
static int me4000_ao_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
int rang = CR_RANGE(insn->chanspec);
int aref = CR_AREF(insn->chanspec);
unsigned long tmp;
CALL_PDEBUG("In me4000_ao_insn_write()\n");
if (insn->n == 0) {
return 0;
} else if (insn->n > 1) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ao_insn_write(): Invalid instruction length %d\n",
dev->minor, insn->n);
return -EINVAL;
}
if (chan >= thisboard->ao.count) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ao_insn_write(): Invalid channel %d\n",
dev->minor, insn->n);
return -EINVAL;
}
if (rang != 0) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ao_insn_write(): Invalid range %d\n",
dev->minor, insn->n);
return -EINVAL;
}
if (aref != AREF_GROUND && aref != AREF_COMMON) {
printk(KERN_ERR
"comedi%d: me4000: me4000_ao_insn_write(): Invalid aref %d\n",
dev->minor, insn->n);
return -EINVAL;
}
/* Stop any running conversion */
tmp = me4000_inl(dev, info->ao_context[chan].ctrl_reg);
tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP;
me4000_outl(dev, tmp, info->ao_context[chan].ctrl_reg);
/* Clear control register and set to single mode */
me4000_outl(dev, 0x0, info->ao_context[chan].ctrl_reg);
/* Write data value */
me4000_outl(dev, data[0], info->ao_context[chan].single_reg);
/* Store in the mirror */
info->ao_context[chan].mirror = data[0];
return 1;
}
static int me4000_ao_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
int chan = CR_CHAN(insn->chanspec);
if (insn->n == 0) {
return 0;
} else if (insn->n > 1) {
printk
("comedi%d: me4000: me4000_ao_insn_read(): Invalid instruction length\n",
dev->minor);
return -EINVAL;
}
data[0] = info->ao_context[chan].mirror;
return 1;
}
/*=============================================================================
Digital I/O section
===========================================================================*/
static int me4000_dio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
CALL_PDEBUG("In me4000_dio_insn_bits()\n");
/* Length of data must be 2 (mask and new data, see below) */
if (insn->n == 0)
return 0;
if (insn->n != 2) {
printk
("comedi%d: me4000: me4000_dio_insn_bits(): Invalid instruction length\n",
dev->minor);
return -EINVAL;
}
/*
* The insn data consists of a mask in data[0] and the new data
* in data[1]. The mask defines which bits we are concerning about.
* The new data must be anded with the mask.
* Each channel corresponds to a bit.
*/
if (data[0]) {
/* Check if requested ports are configured for output */
if ((s->io_bits & data[0]) != data[0])
return -EIO;
s->state &= ~data[0];
s->state |= data[0] & data[1];
/* Write out the new digital output lines */
me4000_outl(dev, (s->state >> 0) & 0xFF,
info->dio_context.port_0_reg);
me4000_outl(dev, (s->state >> 8) & 0xFF,
info->dio_context.port_1_reg);
me4000_outl(dev, (s->state >> 16) & 0xFF,
info->dio_context.port_2_reg);
me4000_outl(dev, (s->state >> 24) & 0xFF,
info->dio_context.port_3_reg);
}
/* On return, data[1] contains the value of
the digital input and output lines. */
data[1] =
((me4000_inl(dev, info->dio_context.port_0_reg) & 0xFF) << 0) |
((me4000_inl(dev, info->dio_context.port_1_reg) & 0xFF) << 8) |
((me4000_inl(dev, info->dio_context.port_2_reg) & 0xFF) << 16) |
((me4000_inl(dev, info->dio_context.port_3_reg) & 0xFF) << 24);
return 2;
}
static int me4000_dio_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
unsigned long tmp;
int chan = CR_CHAN(insn->chanspec);
CALL_PDEBUG("In me4000_dio_insn_config()\n");
if (data[0] == INSN_CONFIG_DIO_QUERY) {
data[1] =
(s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
return insn->n;
}
/*
* The input or output configuration of each digital line is
* configured by a special insn_config instruction. chanspec
* contains the channel to be changed, and data[0] contains the
* value COMEDI_INPUT or COMEDI_OUTPUT.
* On the ME-4000 it is only possible to switch port wise (8 bit)
*/
tmp = me4000_inl(dev, info->dio_context.ctrl_reg);
if (data[0] == COMEDI_OUTPUT) {
if (chan < 8) {
s->io_bits |= 0xFF;
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 |
ME4000_DIO_CTRL_BIT_MODE_1);
tmp |= ME4000_DIO_CTRL_BIT_MODE_0;
} else if (chan < 16) {
/*
* Chech for optoisolated ME-4000 version. If one the first
* port is a fixed output port and the second is a fixed input port.
*/
if (!me4000_inl(dev, info->dio_context.dir_reg))
return -ENODEV;
s->io_bits |= 0xFF00;
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_2 |
ME4000_DIO_CTRL_BIT_MODE_3);
tmp |= ME4000_DIO_CTRL_BIT_MODE_2;
} else if (chan < 24) {
s->io_bits |= 0xFF0000;
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_4 |
ME4000_DIO_CTRL_BIT_MODE_5);
tmp |= ME4000_DIO_CTRL_BIT_MODE_4;
} else if (chan < 32) {
s->io_bits |= 0xFF000000;
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_6 |
ME4000_DIO_CTRL_BIT_MODE_7);
tmp |= ME4000_DIO_CTRL_BIT_MODE_6;
} else {
return -EINVAL;
}
} else {
if (chan < 8) {
/*
* Chech for optoisolated ME-4000 version. If one the first
* port is a fixed output port and the second is a fixed input port.
*/
if (!me4000_inl(dev, info->dio_context.dir_reg))
return -ENODEV;
s->io_bits &= ~0xFF;
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 |
ME4000_DIO_CTRL_BIT_MODE_1);
} else if (chan < 16) {
s->io_bits &= ~0xFF00;
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_2 |
ME4000_DIO_CTRL_BIT_MODE_3);
} else if (chan < 24) {
s->io_bits &= ~0xFF0000;
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_4 |
ME4000_DIO_CTRL_BIT_MODE_5);
} else if (chan < 32) {
s->io_bits &= ~0xFF000000;
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_6 |
ME4000_DIO_CTRL_BIT_MODE_7);
} else {
return -EINVAL;
}
}
me4000_outl(dev, tmp, info->dio_context.ctrl_reg);
return 1;
}
/*=============================================================================
Counter section
===========================================================================*/
static int cnt_reset(struct comedi_device *dev, unsigned int channel)
{
CALL_PDEBUG("In cnt_reset()\n");
switch (channel) {
case 0:
me4000_outb(dev, 0x30, info->cnt_context.ctrl_reg);
me4000_outb(dev, 0x00, info->cnt_context.counter_0_reg);
me4000_outb(dev, 0x00, info->cnt_context.counter_0_reg);
break;
case 1:
me4000_outb(dev, 0x70, info->cnt_context.ctrl_reg);
me4000_outb(dev, 0x00, info->cnt_context.counter_1_reg);
me4000_outb(dev, 0x00, info->cnt_context.counter_1_reg);
break;
case 2:
me4000_outb(dev, 0xB0, info->cnt_context.ctrl_reg);
me4000_outb(dev, 0x00, info->cnt_context.counter_2_reg);
me4000_outb(dev, 0x00, info->cnt_context.counter_2_reg);
break;
default:
printk(KERN_ERR
"comedi%d: me4000: cnt_reset(): Invalid channel\n",
dev->minor);
return -EINVAL;
}
return 0;
}
static int cnt_config(struct comedi_device *dev, unsigned int channel,
unsigned int mode)
{
int tmp = 0;
CALL_PDEBUG("In cnt_config()\n");
switch (channel) {
case 0:
tmp |= ME4000_CNT_COUNTER_0;
break;
case 1:
tmp |= ME4000_CNT_COUNTER_1;
break;
case 2:
tmp |= ME4000_CNT_COUNTER_2;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: cnt_config(): Invalid channel\n",
dev->minor);
return -EINVAL;
}
switch (mode) {
case 0:
tmp |= ME4000_CNT_MODE_0;
break;
case 1:
tmp |= ME4000_CNT_MODE_1;
break;
case 2:
tmp |= ME4000_CNT_MODE_2;
break;
case 3:
tmp |= ME4000_CNT_MODE_3;
break;
case 4:
tmp |= ME4000_CNT_MODE_4;
break;
case 5:
tmp |= ME4000_CNT_MODE_5;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: cnt_config(): Invalid counter mode\n",
dev->minor);
return -EINVAL;
}
/* Write the control word */
tmp |= 0x30;
me4000_outb(dev, tmp, info->cnt_context.ctrl_reg);
return 0;
}
static int me4000_cnt_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
int err;
CALL_PDEBUG("In me4000_cnt_insn_config()\n");
switch (data[0]) {
case GPCT_RESET:
if (insn->n != 1) {
printk(KERN_ERR
"comedi%d: me4000: me4000_cnt_insn_config(): Invalid instruction length%d\n",
dev->minor, insn->n);
return -EINVAL;
}
err = cnt_reset(dev, insn->chanspec);
if (err)
return err;
break;
case GPCT_SET_OPERATION:
if (insn->n != 2) {
printk(KERN_ERR
"comedi%d: me4000: me4000_cnt_insn_config(): Invalid instruction length%d\n",
dev->minor, insn->n);
return -EINVAL;
}
err = cnt_config(dev, insn->chanspec, data[1]);
if (err)
return err;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_cnt_insn_config(): Invalid instruction\n",
dev->minor);
return -EINVAL;
}
return 2;
}
static int me4000_cnt_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
unsigned short tmp;
CALL_PDEBUG("In me4000_cnt_insn_read()\n");
if (insn->n == 0)
return 0;
if (insn->n > 1) {
printk(KERN_ERR
"comedi%d: me4000: me4000_cnt_insn_read(): Invalid instruction length %d\n",
dev->minor, insn->n);
return -EINVAL;
}
switch (insn->chanspec) {
case 0:
tmp = me4000_inb(dev, info->cnt_context.counter_0_reg);
data[0] = tmp;
tmp = me4000_inb(dev, info->cnt_context.counter_0_reg);
data[0] |= tmp << 8;
break;
case 1:
tmp = me4000_inb(dev, info->cnt_context.counter_1_reg);
data[0] = tmp;
tmp = me4000_inb(dev, info->cnt_context.counter_1_reg);
data[0] |= tmp << 8;
break;
case 2:
tmp = me4000_inb(dev, info->cnt_context.counter_2_reg);
data[0] = tmp;
tmp = me4000_inb(dev, info->cnt_context.counter_2_reg);
data[0] |= tmp << 8;
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_cnt_insn_read(): Invalid channel %d\n",
dev->minor, insn->chanspec);
return -EINVAL;
}
return 1;
}
static int me4000_cnt_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
unsigned short tmp;
CALL_PDEBUG("In me4000_cnt_insn_write()\n");
if (insn->n == 0) {
return 0;
} else if (insn->n > 1) {
printk(KERN_ERR
"comedi%d: me4000: me4000_cnt_insn_write(): Invalid instruction length %d\n",
dev->minor, insn->n);
return -EINVAL;
}
switch (insn->chanspec) {
case 0:
tmp = data[0] & 0xFF;
me4000_outb(dev, tmp, info->cnt_context.counter_0_reg);
tmp = (data[0] >> 8) & 0xFF;
me4000_outb(dev, tmp, info->cnt_context.counter_0_reg);
break;
case 1:
tmp = data[0] & 0xFF;
me4000_outb(dev, tmp, info->cnt_context.counter_1_reg);
tmp = (data[0] >> 8) & 0xFF;
me4000_outb(dev, tmp, info->cnt_context.counter_1_reg);
break;
case 2:
tmp = data[0] & 0xFF;
me4000_outb(dev, tmp, info->cnt_context.counter_2_reg);
tmp = (data[0] >> 8) & 0xFF;
me4000_outb(dev, tmp, info->cnt_context.counter_2_reg);
break;
default:
printk(KERN_ERR
"comedi%d: me4000: me4000_cnt_insn_write(): Invalid channel %d\n",
dev->minor, insn->chanspec);
return -EINVAL;
}
return 1;
}
COMEDI_PCI_INITCLEANUP(driver_me4000, me4000_pci_table);