1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2*8ffdff6aSGreg Kroah-Hartman /* 3*8ffdff6aSGreg Kroah-Hartman * dac02.c 4*8ffdff6aSGreg Kroah-Hartman * Comedi driver for DAC02 compatible boards 5*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com> 6*8ffdff6aSGreg Kroah-Hartman * 7*8ffdff6aSGreg Kroah-Hartman * Based on the poc driver 8*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net> 9*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 2001 David A. Schleef <ds@schleef.org> 10*8ffdff6aSGreg Kroah-Hartman * 11*8ffdff6aSGreg Kroah-Hartman * COMEDI - Linux Control and Measurement Device Interface 12*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 1998 David A. Schleef <ds@schleef.org> 13*8ffdff6aSGreg Kroah-Hartman */ 14*8ffdff6aSGreg Kroah-Hartman 15*8ffdff6aSGreg Kroah-Hartman /* 16*8ffdff6aSGreg Kroah-Hartman * Driver: dac02 17*8ffdff6aSGreg Kroah-Hartman * Description: Comedi driver for DAC02 compatible boards 18*8ffdff6aSGreg Kroah-Hartman * Devices: [Keithley Metrabyte] DAC-02 (dac02) 19*8ffdff6aSGreg Kroah-Hartman * Author: H Hartley Sweeten <hsweeten@visionengravers.com> 20*8ffdff6aSGreg Kroah-Hartman * Updated: Tue, 11 Mar 2014 11:27:19 -0700 21*8ffdff6aSGreg Kroah-Hartman * Status: unknown 22*8ffdff6aSGreg Kroah-Hartman * 23*8ffdff6aSGreg Kroah-Hartman * Configuration options: 24*8ffdff6aSGreg Kroah-Hartman * [0] - I/O port base 25*8ffdff6aSGreg Kroah-Hartman */ 26*8ffdff6aSGreg Kroah-Hartman 27*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 28*8ffdff6aSGreg Kroah-Hartman 29*8ffdff6aSGreg Kroah-Hartman #include "../comedidev.h" 30*8ffdff6aSGreg Kroah-Hartman 31*8ffdff6aSGreg Kroah-Hartman /* 32*8ffdff6aSGreg Kroah-Hartman * The output range is selected by jumpering pins on the I/O connector. 33*8ffdff6aSGreg Kroah-Hartman * 34*8ffdff6aSGreg Kroah-Hartman * Range Chan # Jumper pins Output 35*8ffdff6aSGreg Kroah-Hartman * ------------- ------ ------------- ----------------- 36*8ffdff6aSGreg Kroah-Hartman * 0 to 5V 0 21 to 22 24 37*8ffdff6aSGreg Kroah-Hartman * 1 15 to 16 18 38*8ffdff6aSGreg Kroah-Hartman * 0 to 10V 0 20 to 22 24 39*8ffdff6aSGreg Kroah-Hartman * 1 14 to 16 18 40*8ffdff6aSGreg Kroah-Hartman * +/-5V 0 21 to 22 23 41*8ffdff6aSGreg Kroah-Hartman * 1 15 to 16 17 42*8ffdff6aSGreg Kroah-Hartman * +/-10V 0 20 to 22 23 43*8ffdff6aSGreg Kroah-Hartman * 1 14 to 16 17 44*8ffdff6aSGreg Kroah-Hartman * 4 to 20mA 0 21 to 22 25 45*8ffdff6aSGreg Kroah-Hartman * 1 15 to 16 19 46*8ffdff6aSGreg Kroah-Hartman * AC reference 0 In on pin 22 24 (2-quadrant) 47*8ffdff6aSGreg Kroah-Hartman * In on pin 22 23 (4-quadrant) 48*8ffdff6aSGreg Kroah-Hartman * 1 In on pin 16 18 (2-quadrant) 49*8ffdff6aSGreg Kroah-Hartman * In on pin 16 17 (4-quadrant) 50*8ffdff6aSGreg Kroah-Hartman */ 51*8ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange das02_ao_ranges = { 52*8ffdff6aSGreg Kroah-Hartman 6, { 53*8ffdff6aSGreg Kroah-Hartman UNI_RANGE(5), 54*8ffdff6aSGreg Kroah-Hartman UNI_RANGE(10), 55*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(5), 56*8ffdff6aSGreg Kroah-Hartman BIP_RANGE(10), 57*8ffdff6aSGreg Kroah-Hartman RANGE_mA(4, 20), 58*8ffdff6aSGreg Kroah-Hartman RANGE_ext(0, 1) 59*8ffdff6aSGreg Kroah-Hartman } 60*8ffdff6aSGreg Kroah-Hartman }; 61*8ffdff6aSGreg Kroah-Hartman 62*8ffdff6aSGreg Kroah-Hartman /* 63*8ffdff6aSGreg Kroah-Hartman * Register I/O map 64*8ffdff6aSGreg Kroah-Hartman */ 65*8ffdff6aSGreg Kroah-Hartman #define DAC02_AO_LSB(x) (0x00 + ((x) * 2)) 66*8ffdff6aSGreg Kroah-Hartman #define DAC02_AO_MSB(x) (0x01 + ((x) * 2)) 67*8ffdff6aSGreg Kroah-Hartman 68*8ffdff6aSGreg Kroah-Hartman static int dac02_ao_insn_write(struct comedi_device *dev, 69*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 70*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 71*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 72*8ffdff6aSGreg Kroah-Hartman { 73*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 74*8ffdff6aSGreg Kroah-Hartman unsigned int range = CR_RANGE(insn->chanspec); 75*8ffdff6aSGreg Kroah-Hartman unsigned int val; 76*8ffdff6aSGreg Kroah-Hartman int i; 77*8ffdff6aSGreg Kroah-Hartman 78*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) { 79*8ffdff6aSGreg Kroah-Hartman val = data[i]; 80*8ffdff6aSGreg Kroah-Hartman 81*8ffdff6aSGreg Kroah-Hartman s->readback[chan] = val; 82*8ffdff6aSGreg Kroah-Hartman 83*8ffdff6aSGreg Kroah-Hartman /* 84*8ffdff6aSGreg Kroah-Hartman * Unipolar outputs are true binary encoding. 85*8ffdff6aSGreg Kroah-Hartman * Bipolar outputs are complementary offset binary 86*8ffdff6aSGreg Kroah-Hartman * (that is, 0 = +full scale, maxdata = -full scale). 87*8ffdff6aSGreg Kroah-Hartman */ 88*8ffdff6aSGreg Kroah-Hartman if (comedi_range_is_bipolar(s, range)) 89*8ffdff6aSGreg Kroah-Hartman val = s->maxdata - val; 90*8ffdff6aSGreg Kroah-Hartman 91*8ffdff6aSGreg Kroah-Hartman /* 92*8ffdff6aSGreg Kroah-Hartman * DACs are double-buffered. 93*8ffdff6aSGreg Kroah-Hartman * Write LSB then MSB to latch output. 94*8ffdff6aSGreg Kroah-Hartman */ 95*8ffdff6aSGreg Kroah-Hartman outb((val << 4) & 0xf0, dev->iobase + DAC02_AO_LSB(chan)); 96*8ffdff6aSGreg Kroah-Hartman outb((val >> 4) & 0xff, dev->iobase + DAC02_AO_MSB(chan)); 97*8ffdff6aSGreg Kroah-Hartman } 98*8ffdff6aSGreg Kroah-Hartman 99*8ffdff6aSGreg Kroah-Hartman return insn->n; 100*8ffdff6aSGreg Kroah-Hartman } 101*8ffdff6aSGreg Kroah-Hartman 102*8ffdff6aSGreg Kroah-Hartman static int dac02_attach(struct comedi_device *dev, struct comedi_devconfig *it) 103*8ffdff6aSGreg Kroah-Hartman { 104*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 105*8ffdff6aSGreg Kroah-Hartman int ret; 106*8ffdff6aSGreg Kroah-Hartman 107*8ffdff6aSGreg Kroah-Hartman ret = comedi_request_region(dev, it->options[0], 0x08); 108*8ffdff6aSGreg Kroah-Hartman if (ret) 109*8ffdff6aSGreg Kroah-Hartman return ret; 110*8ffdff6aSGreg Kroah-Hartman 111*8ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 1); 112*8ffdff6aSGreg Kroah-Hartman if (ret) 113*8ffdff6aSGreg Kroah-Hartman return ret; 114*8ffdff6aSGreg Kroah-Hartman 115*8ffdff6aSGreg Kroah-Hartman /* Analog Output subdevice */ 116*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0]; 117*8ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AO; 118*8ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_WRITABLE; 119*8ffdff6aSGreg Kroah-Hartman s->n_chan = 2; 120*8ffdff6aSGreg Kroah-Hartman s->maxdata = 0x0fff; 121*8ffdff6aSGreg Kroah-Hartman s->range_table = &das02_ao_ranges; 122*8ffdff6aSGreg Kroah-Hartman s->insn_write = dac02_ao_insn_write; 123*8ffdff6aSGreg Kroah-Hartman 124*8ffdff6aSGreg Kroah-Hartman return comedi_alloc_subdev_readback(s); 125*8ffdff6aSGreg Kroah-Hartman } 126*8ffdff6aSGreg Kroah-Hartman 127*8ffdff6aSGreg Kroah-Hartman static struct comedi_driver dac02_driver = { 128*8ffdff6aSGreg Kroah-Hartman .driver_name = "dac02", 129*8ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE, 130*8ffdff6aSGreg Kroah-Hartman .attach = dac02_attach, 131*8ffdff6aSGreg Kroah-Hartman .detach = comedi_legacy_detach, 132*8ffdff6aSGreg Kroah-Hartman }; 133*8ffdff6aSGreg Kroah-Hartman module_comedi_driver(dac02_driver); 134*8ffdff6aSGreg Kroah-Hartman 135*8ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 136*8ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi driver for DAC02 compatible boards"); 137*8ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL"); 138