Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/comedi/drivers/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 11 kB image not shown  

Quelle  adv_pci1760.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * COMEDI driver for the Advantech PCI-1760
 * Copyright (C) 2015 H Hartley Sweeten <hsweeten@visionengravers.com>
 *
 * Based on the pci1760 support in the adv_pci_dio driver written by:
 * Michal Dobes <dobes@tesnet.cz>
 *
 * COMEDI - Linux Control and Measurement Device Interface
 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
 */


/*
 * Driver: adv_pci1760
 * Description: Advantech PCI-1760 Relay & Isolated Digital Input Card
 * Devices: [Advantech] PCI-1760 (adv_pci1760)
 * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
 * Updated: Fri, 13 Nov 2015 12:34:00 -0700
 * Status: untested
 *
 * Configuration Options: not applicable, uses PCI auto config
 */


#include <linux/module.h>
#include <linux/comedi/comedi_pci.h>

/*
 * PCI-1760 Register Map
 *
 * Outgoing Mailbox Bytes
 * OMB3: Not used (must be 0)
 * OMB2: The command code to the PCI-1760
 * OMB1: The hi byte of the parameter for the command in OMB2
 * OMB0: The lo byte of the parameter for the command in OMB2
 *
 * Incoming Mailbox Bytes
 * IMB3: The Isolated Digital Input status (updated every 100us)
 * IMB2: The current command (matches OMB2 when command is successful)
 * IMB1: The hi byte of the feedback data for the command in OMB2
 * IMB0: The lo byte of the feedback data for the command in OMB2
 *
 * Interrupt Control/Status
 * INTCSR3: Not used (must be 0)
 * INTCSR2: The interrupt status (read only)
 * INTCSR1: Interrupt enable/disable
 * INTCSR0: Not used (must be 0)
 */

#define PCI1760_OMB_REG(x)  (0x0c + (x))
#define PCI1760_IMB_REG(x)  (0x1c + (x))
#define PCI1760_INTCSR_REG(x)  (0x38 + (x))
#define PCI1760_INTCSR1_IRQ_ENA  BIT(5)
#define PCI1760_INTCSR2_OMB_IRQ  BIT(0)
#define PCI1760_INTCSR2_IMB_IRQ  BIT(1)
#define PCI1760_INTCSR2_IRQ_STATUS BIT(6)
#define PCI1760_INTCSR2_IRQ_ASSERTED BIT(7)

/* PCI-1760 command codes */
#define PCI1760_CMD_CLR_IMB2  0x00 /* Clears IMB2 */
#define PCI1760_CMD_SET_DO  0x01 /* Set output state */
#define PCI1760_CMD_GET_DO  0x02 /* Read output status */
#define PCI1760_CMD_GET_STATUS  0x07 /* Read current status */
#define PCI1760_CMD_GET_FW_VER  0x0e /* Read firmware version */
#define PCI1760_CMD_GET_HW_VER  0x0f /* Read hardware version */
#define PCI1760_CMD_SET_PWM_HI(x) (0x10 + (x) * 2) /* Set "hi" period */
#define PCI1760_CMD_SET_PWM_LO(x) (0x11 + (x) * 2) /* Set "lo" period */
#define PCI1760_CMD_SET_PWM_CNT(x) (0x14 + (x)) /* Set burst count */
#define PCI1760_CMD_ENA_PWM  0x1f /* Enable PWM outputs */
#define PCI1760_CMD_ENA_FILT  0x20 /* Enable input filter */
#define PCI1760_CMD_ENA_PAT_MATCH 0x21 /* Enable input pattern match */
#define PCI1760_CMD_SET_PAT_MATCH 0x22 /* Set input pattern match */
#define PCI1760_CMD_ENA_RISE_EDGE 0x23 /* Enable input rising edge */
#define PCI1760_CMD_ENA_FALL_EDGE 0x24 /* Enable input falling edge */
#define PCI1760_CMD_ENA_CNT  0x28 /* Enable counter */
#define PCI1760_CMD_RST_CNT  0x29 /* Reset counter */
#define PCI1760_CMD_ENA_CNT_OFLOW 0x2a /* Enable counter overflow */
#define PCI1760_CMD_ENA_CNT_MATCH 0x2b /* Enable counter match */
#define PCI1760_CMD_SET_CNT_EDGE 0x2c /* Set counter edge */
#define PCI1760_CMD_GET_CNT  0x2f /* Reads counter value */
#define PCI1760_CMD_SET_HI_SAMP(x) (0x30 + (x)) /* Set "hi" sample time */
#define PCI1760_CMD_SET_LO_SAMP(x) (0x38 + (x)) /* Set "lo" sample time */
#define PCI1760_CMD_SET_CNT(x)  (0x40 + (x)) /* Set counter reset val */
#define PCI1760_CMD_SET_CNT_MATCH(x) (0x48 + (x)) /* Set counter match val */
#define PCI1760_CMD_GET_INT_FLAGS 0x60 /* Read interrupt flags */
#define PCI1760_CMD_GET_INT_FLAGS_MATCH BIT(0)
#define PCI1760_CMD_GET_INT_FLAGS_COS BIT(1)
#define PCI1760_CMD_GET_INT_FLAGS_OFLOW BIT(2)
#define PCI1760_CMD_GET_OS  0x61 /* Read edge change flags */
#define PCI1760_CMD_GET_CNT_STATUS 0x62 /* Read counter oflow/match */

#define PCI1760_CMD_TIMEOUT  250 /* 250 usec timeout */
#define PCI1760_CMD_RETRIES  3 /* limit number of retries */

#define PCI1760_PWM_TIMEBASE  100000 /* 1 unit = 100 usec */

static int pci1760_send_cmd(struct comedi_device *dev,
       unsigned char cmd, unsigned short val)
{
 unsigned long timeout;

 /* send the command and parameter */
 outb(val & 0xff, dev->iobase + PCI1760_OMB_REG(0));
 outb((val >> 8) & 0xff, dev->iobase + PCI1760_OMB_REG(1));
 outb(cmd, dev->iobase + PCI1760_OMB_REG(2));
 outb(0, dev->iobase + PCI1760_OMB_REG(3));

 /* datasheet says to allow up to 250 usec for the command to complete */
 timeout = jiffies + usecs_to_jiffies(PCI1760_CMD_TIMEOUT);
 do {
  if (inb(dev->iobase + PCI1760_IMB_REG(2)) == cmd) {
   /* command success; return the feedback data */
   return inb(dev->iobase + PCI1760_IMB_REG(0)) |
          (inb(dev->iobase + PCI1760_IMB_REG(1)) << 8);
  }
  cpu_relax();
 } while (time_before(jiffies, timeout));

 return -EBUSY;
}

static int pci1760_cmd(struct comedi_device *dev,
         unsigned char cmd, unsigned short val)
{
 int repeats;
 int ret;

 /* send PCI1760_CMD_CLR_IMB2 between identical commands */
 if (inb(dev->iobase + PCI1760_IMB_REG(2)) == cmd) {
  ret = pci1760_send_cmd(dev, PCI1760_CMD_CLR_IMB2, 0);
  if (ret < 0) {
   /* timeout? try it once more */
   ret = pci1760_send_cmd(dev, PCI1760_CMD_CLR_IMB2, 0);
   if (ret < 0)
    return -ETIMEDOUT;
  }
 }

 /* datasheet says to keep retrying the command */
 for (repeats = 0; repeats < PCI1760_CMD_RETRIES; repeats++) {
  ret = pci1760_send_cmd(dev, cmd, val);
  if (ret >= 0)
   return ret;
 }

 /* command failed! */
 return -ETIMEDOUT;
}

static int pci1760_di_insn_bits(struct comedi_device *dev,
    struct comedi_subdevice *s,
    struct comedi_insn *insn,
    unsigned int *data)
{
 data[1] = inb(dev->iobase + PCI1760_IMB_REG(3));

 return insn->n;
}

static int pci1760_do_insn_bits(struct comedi_device *dev,
    struct comedi_subdevice *s,
    struct comedi_insn *insn,
    unsigned int *data)
{
 int ret;

 if (comedi_dio_update_state(s, data)) {
  ret = pci1760_cmd(dev, PCI1760_CMD_SET_DO, s->state);
  if (ret < 0)
   return ret;
 }

 data[1] = s->state;

 return insn->n;
}

static int pci1760_pwm_ns_to_div(unsigned int flags, unsigned int ns)
{
 unsigned int divisor;

 switch (flags) {
 case CMDF_ROUND_NEAREST:
  divisor = DIV_ROUND_CLOSEST(ns, PCI1760_PWM_TIMEBASE);
  break;
 case CMDF_ROUND_UP:
  divisor = DIV_ROUND_UP(ns, PCI1760_PWM_TIMEBASE);
  break;
 case CMDF_ROUND_DOWN:
  divisor = ns / PCI1760_PWM_TIMEBASE;
  break;
 default:
  return -EINVAL;
 }

 if (divisor < 1)
  divisor = 1;
 if (divisor > 0xffff)
  divisor = 0xffff;

 return divisor;
}

static int pci1760_pwm_enable(struct comedi_device *dev,
         unsigned int chan, bool enable)
{
 int ret;

 ret = pci1760_cmd(dev, PCI1760_CMD_GET_STATUS, PCI1760_CMD_ENA_PWM);
 if (ret < 0)
  return ret;

 if (enable)
  ret |= BIT(chan);
 else
  ret &= ~BIT(chan);

 return pci1760_cmd(dev, PCI1760_CMD_ENA_PWM, ret);
}

static int pci1760_pwm_insn_config(struct comedi_device *dev,
       struct comedi_subdevice *s,
       struct comedi_insn *insn,
       unsigned int *data)
{
 unsigned int chan = CR_CHAN(insn->chanspec);
 int hi_div;
 int lo_div;
 int ret;

 switch (data[0]) {
 case INSN_CONFIG_ARM:
  ret = pci1760_pwm_enable(dev, chan, false);
  if (ret < 0)
   return ret;

  if (data[1] > 0xffff)
   return -EINVAL;
  ret = pci1760_cmd(dev, PCI1760_CMD_SET_PWM_CNT(chan), data[1]);
  if (ret < 0)
   return ret;

  ret = pci1760_pwm_enable(dev, chan, true);
  if (ret < 0)
   return ret;
  break;
 case INSN_CONFIG_DISARM:
  ret = pci1760_pwm_enable(dev, chan, false);
  if (ret < 0)
   return ret;
  break;
 case INSN_CONFIG_PWM_OUTPUT:
  ret = pci1760_pwm_enable(dev, chan, false);
  if (ret < 0)
   return ret;

  hi_div = pci1760_pwm_ns_to_div(data[1], data[2]);
  lo_div = pci1760_pwm_ns_to_div(data[3], data[4]);
  if (hi_div < 0 || lo_div < 0)
   return -EINVAL;
  if ((hi_div * PCI1760_PWM_TIMEBASE) != data[2] ||
      (lo_div * PCI1760_PWM_TIMEBASE) != data[4]) {
   data[2] = hi_div * PCI1760_PWM_TIMEBASE;
   data[4] = lo_div * PCI1760_PWM_TIMEBASE;
   return -EAGAIN;
  }
  ret = pci1760_cmd(dev, PCI1760_CMD_SET_PWM_HI(chan), hi_div);
  if (ret < 0)
   return ret;
  ret = pci1760_cmd(dev, PCI1760_CMD_SET_PWM_LO(chan), lo_div);
  if (ret < 0)
   return ret;
  break;
 case INSN_CONFIG_GET_PWM_OUTPUT:
  hi_div = pci1760_cmd(dev, PCI1760_CMD_GET_STATUS,
         PCI1760_CMD_SET_PWM_HI(chan));
  lo_div = pci1760_cmd(dev, PCI1760_CMD_GET_STATUS,
         PCI1760_CMD_SET_PWM_LO(chan));
  if (hi_div < 0 || lo_div < 0)
   return -ETIMEDOUT;

  data[1] = hi_div * PCI1760_PWM_TIMEBASE;
  data[2] = lo_div * PCI1760_PWM_TIMEBASE;
  break;
 case INSN_CONFIG_GET_PWM_STATUS:
  ret = pci1760_cmd(dev, PCI1760_CMD_GET_STATUS,
      PCI1760_CMD_ENA_PWM);
  if (ret < 0)
   return ret;

  data[1] = (ret & BIT(chan)) ? 1 : 0;
  break;
 default:
  return -EINVAL;
 }

 return insn->n;
}

static void pci1760_reset(struct comedi_device *dev)
{
 int i;

 /* disable interrupts (intcsr2 is read-only) */
 outb(0, dev->iobase + PCI1760_INTCSR_REG(0));
 outb(0, dev->iobase + PCI1760_INTCSR_REG(1));
 outb(0, dev->iobase + PCI1760_INTCSR_REG(3));

 /* disable counters */
 pci1760_cmd(dev, PCI1760_CMD_ENA_CNT, 0);

 /* disable overflow interrupts */
 pci1760_cmd(dev, PCI1760_CMD_ENA_CNT_OFLOW, 0);

 /* disable match */
 pci1760_cmd(dev, PCI1760_CMD_ENA_CNT_MATCH, 0);

 /* set match and counter reset values */
 for (i = 0; i < 8; i++) {
  pci1760_cmd(dev, PCI1760_CMD_SET_CNT_MATCH(i), 0x8000);
  pci1760_cmd(dev, PCI1760_CMD_SET_CNT(i), 0x0000);
 }

 /* reset counters to reset values */
 pci1760_cmd(dev, PCI1760_CMD_RST_CNT, 0xff);

 /* set counter count edges */
 pci1760_cmd(dev, PCI1760_CMD_SET_CNT_EDGE, 0);

 /* disable input filters */
 pci1760_cmd(dev, PCI1760_CMD_ENA_FILT, 0);

 /* disable pattern matching */
 pci1760_cmd(dev, PCI1760_CMD_ENA_PAT_MATCH, 0);

 /* set pattern match value */
 pci1760_cmd(dev, PCI1760_CMD_SET_PAT_MATCH, 0);
}

static int pci1760_auto_attach(struct comedi_device *dev,
          unsigned long context)
{
 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 struct comedi_subdevice *s;
 int ret;

 ret = comedi_pci_enable(dev);
 if (ret)
  return ret;
 dev->iobase = pci_resource_start(pcidev, 0);

 pci1760_reset(dev);

 ret = comedi_alloc_subdevices(dev, 4);
 if (ret)
  return ret;

 /* Digital Input subdevice */
 s = &dev->subdevices[0];
 s->type  = COMEDI_SUBD_DI;
 s->subdev_flags = SDF_READABLE;
 s->n_chan = 8;
 s->maxdata = 1;
 s->range_table = &range_digital;
 s->insn_bits = pci1760_di_insn_bits;

 /* Digital Output subdevice */
 s = &dev->subdevices[1];
 s->type  = COMEDI_SUBD_DO;
 s->subdev_flags = SDF_WRITABLE;
 s->n_chan = 8;
 s->maxdata = 1;
 s->range_table = &range_digital;
 s->insn_bits = pci1760_do_insn_bits;

 /* get the current state of the outputs */
 ret = pci1760_cmd(dev, PCI1760_CMD_GET_DO, 0);
 if (ret < 0)
  return ret;
 s->state = ret;

 /* PWM subdevice */
 s = &dev->subdevices[2];
 s->type  = COMEDI_SUBD_PWM;
 s->subdev_flags = SDF_PWM_COUNTER;
 s->n_chan = 2;
 s->insn_config = pci1760_pwm_insn_config;

 /* Counter subdevice */
 s = &dev->subdevices[3];
 s->type  = COMEDI_SUBD_UNUSED;

 return 0;
}

static struct comedi_driver pci1760_driver = {
 .driver_name = "adv_pci1760",
 .module  = THIS_MODULE,
 .auto_attach = pci1760_auto_attach,
 .detach  = comedi_pci_detach,
};

static int pci1760_pci_probe(struct pci_dev *dev,
        const struct pci_device_id *id)
{
 return comedi_pci_auto_config(dev, &pci1760_driver, id->driver_data);
}

static const struct pci_device_id pci1760_pci_table[] = {
 { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1760) },
 { 0 }
};
MODULE_DEVICE_TABLE(pci, pci1760_pci_table);

static struct pci_driver pci1760_pci_driver = {
 .name  = "adv_pci1760",
 .id_table = pci1760_pci_table,
 .probe  = pci1760_pci_probe,
 .remove  = comedi_pci_auto_unconfig,
};
module_comedi_pci_driver(pci1760_driver, pci1760_pci_driver);

MODULE_AUTHOR("Comedi https://www.comedi.org");
MODULE_DESCRIPTION("Comedi driver for Advantech PCI-1760");
MODULE_LICENSE("GPL");

Messung V0.5
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.