// SPDX-License-Identifier: GPL-2.0+ /* * Comedi driver for NI 670x devices * * COMEDI - Linux Control and Measurement Device Interface * Copyright (C) 1997-2001 David A. Schleef */ /* * Driver: ni_670x * Description: National Instruments 670x * Author: Bart Joris * Updated: Wed, 11 Dec 2002 18:25:35 -0800 * Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704 * Status: unknown * * Commands are not supported. * * Manuals: * 322110a.pdf PCI/PXI-6704 User Manual * 322110b.pdf PCI/PXI-6703/6704 User Manual */ #include #include #include #include "../comedi_pci.h" #define AO_VALUE_OFFSET 0x00 #define AO_CHAN_OFFSET 0x0c #define AO_STATUS_OFFSET 0x10 #define AO_CONTROL_OFFSET 0x10 #define DIO_PORT0_DIR_OFFSET 0x20 #define DIO_PORT0_DATA_OFFSET 0x24 #define DIO_PORT1_DIR_OFFSET 0x28 #define DIO_PORT1_DATA_OFFSET 0x2c #define MISC_STATUS_OFFSET 0x14 #define MISC_CONTROL_OFFSET 0x14 enum ni_670x_boardid { BOARD_PCI6703, BOARD_PXI6704, BOARD_PCI6704, }; struct ni_670x_board { const char *name; unsigned short ao_chans; }; static const struct ni_670x_board ni_670x_boards[] = { [BOARD_PCI6703] = { .name = "PCI-6703", .ao_chans = 16, }, [BOARD_PXI6704] = { .name = "PXI-6704", .ao_chans = 32, }, [BOARD_PCI6704] = { .name = "PCI-6704", .ao_chans = 32, }, }; struct ni_670x_private { int boardtype; int dio; }; static int ni_670x_ao_insn_write(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { unsigned int chan = CR_CHAN(insn->chanspec); unsigned int val = s->readback[chan]; int i; /* * Channel number mapping: * * NI 6703/ NI 6704 | NI 6704 Only * ------------------------------- * vch(0) : 0 | ich(16) : 1 * vch(1) : 2 | ich(17) : 3 * ... | ... * vch(15) : 30 | ich(31) : 31 */ for (i = 0; i < insn->n; i++) { val = data[i]; /* First write in channel register which channel to use */ writel(((chan & 15) << 1) | ((chan & 16) >> 4), dev->mmio + AO_CHAN_OFFSET); /* write channel value */ writel(val, dev->mmio + AO_VALUE_OFFSET); } s->readback[chan] = val; return insn->n; } static int ni_670x_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { if (comedi_dio_update_state(s, data)) writel(s->state, dev->mmio + DIO_PORT0_DATA_OFFSET); data[1] = readl(dev->mmio + DIO_PORT0_DATA_OFFSET); return insn->n; } static int ni_670x_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { int ret; ret = comedi_dio_insn_config(dev, s, insn, data, 0); if (ret) return ret; writel(s->io_bits, dev->mmio + DIO_PORT0_DIR_OFFSET); return insn->n; } /* ripped from mite.h and mite_setup2() to avoid mite dependency */ #define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */ #define WENAB BIT(7) /* window enable */ static int ni_670x_mite_init(struct pci_dev *pcidev) { void __iomem *mite_base; u32 main_phys_addr; /* ioremap the MITE registers (BAR 0) temporarily */ mite_base = pci_ioremap_bar(pcidev, 0); if (!mite_base) return -ENOMEM; /* set data window to main registers (BAR 1) */ main_phys_addr = pci_resource_start(pcidev, 1); writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR); /* finished with MITE registers */ iounmap(mite_base); return 0; } static int ni_670x_auto_attach(struct comedi_device *dev, unsigned long context) { struct pci_dev *pcidev = comedi_to_pci_dev(dev); const struct ni_670x_board *board = NULL; struct ni_670x_private *devpriv; struct comedi_subdevice *s; int ret; int i; if (context < ARRAY_SIZE(ni_670x_boards)) board = &ni_670x_boards[context]; if (!board) return -ENODEV; dev->board_ptr = board; dev->board_name = board->name; ret = comedi_pci_enable(dev); if (ret) return ret; devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); if (!devpriv) return -ENOMEM; ret = ni_670x_mite_init(pcidev); if (ret) return ret; dev->mmio = pci_ioremap_bar(pcidev, 1); if (!dev->mmio) return -ENOMEM; ret = comedi_alloc_subdevices(dev, 2); if (ret) return ret; s = &dev->subdevices[0]; /* analog output subdevice */ s->type = COMEDI_SUBD_AO; s->subdev_flags = SDF_WRITABLE; s->n_chan = board->ao_chans; s->maxdata = 0xffff; if (s->n_chan == 32) { const struct comedi_lrange **range_table_list; range_table_list = kmalloc_array(32, sizeof(struct comedi_lrange *), GFP_KERNEL); if (!range_table_list) return -ENOMEM; s->range_table_list = range_table_list; for (i = 0; i < 16; i++) { range_table_list[i] = &range_bipolar10; range_table_list[16 + i] = &range_0_20mA; } } else { s->range_table = &range_bipolar10; } s->insn_write = ni_670x_ao_insn_write; ret = comedi_alloc_subdev_readback(s); if (ret) return ret; s = &dev->subdevices[1]; /* digital i/o subdevice */ s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_READABLE | SDF_WRITABLE; s->n_chan = 8; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = ni_670x_dio_insn_bits; s->insn_config = ni_670x_dio_insn_config; /* Config of misc registers */ writel(0x10, dev->mmio + MISC_CONTROL_OFFSET); /* Config of ao registers */ writel(0x00, dev->mmio + AO_CONTROL_OFFSET); return 0; } static void ni_670x_detach(struct comedi_device *dev) { struct comedi_subdevice *s; comedi_pci_detach(dev); if (dev->n_subdevices) { s = &dev->subdevices[0]; if (s) kfree(s->range_table_list); } } static struct comedi_driver ni_670x_driver = { .driver_name = "ni_670x", .module = THIS_MODULE, .auto_attach = ni_670x_auto_attach, .detach = ni_670x_detach, }; static int ni_670x_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { return comedi_pci_auto_config(dev, &ni_670x_driver, id->driver_data); } static const struct pci_device_id ni_670x_pci_table[] = { { PCI_VDEVICE(NI, 0x1290), BOARD_PCI6704 }, { PCI_VDEVICE(NI, 0x1920), BOARD_PXI6704 }, { PCI_VDEVICE(NI, 0x2c90), BOARD_PCI6703 }, { 0 } }; MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); static struct pci_driver ni_670x_pci_driver = { .name = "ni_670x", .id_table = ni_670x_pci_table, .probe = ni_670x_pci_probe, .remove = comedi_pci_auto_unconfig, }; module_comedi_pci_driver(ni_670x_driver, ni_670x_pci_driver); MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); MODULE_LICENSE("GPL");