1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * ssv_dnp.c 4 * generic comedi driver for SSV Embedded Systems' DIL/Net-PCs 5 * Copyright (C) 2001 Robert Schwebel <robert@schwebel.de> 6 * 7 * COMEDI - Linux Control and Measurement Device Interface 8 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 9 */ 10 11 /* 12 * Driver: ssv_dnp 13 * Description: SSV Embedded Systems DIL/Net-PC 14 * Author: Robert Schwebel <robert@schwebel.de> 15 * Devices: [SSV Embedded Systems] DIL/Net-PC 1486 (dnp-1486) 16 * Status: unknown 17 */ 18 19 /* include files ----------------------------------------------------------- */ 20 21 #include <linux/module.h> 22 #include <linux/comedi/comedidev.h> 23 24 /* Some global definitions: the registers of the DNP ----------------------- */ 25 /* */ 26 /* For port A and B the mode register has bits corresponding to the output */ 27 /* pins, where Bit-N = 0 -> input, Bit-N = 1 -> output. Note that bits */ 28 /* 4 to 7 correspond to pin 0..3 for port C data register. Ensure that bits */ 29 /* 0..3 remain unchanged! For details about Port C Mode Register see */ 30 /* the remarks in dnp_insn_config() below. */ 31 32 #define CSCIR 0x22 /* Chip Setup and Control Index Register */ 33 #define CSCDR 0x23 /* Chip Setup and Control Data Register */ 34 #define PAMR 0xa5 /* Port A Mode Register */ 35 #define PADR 0xa9 /* Port A Data Register */ 36 #define PBMR 0xa4 /* Port B Mode Register */ 37 #define PBDR 0xa8 /* Port B Data Register */ 38 #define PCMR 0xa3 /* Port C Mode Register */ 39 #define PCDR 0xa7 /* Port C Data Register */ 40 41 static int dnp_dio_insn_bits(struct comedi_device *dev, 42 struct comedi_subdevice *s, 43 struct comedi_insn *insn, 44 unsigned int *data) 45 { 46 unsigned int mask; 47 unsigned int val; 48 49 /* 50 * Ports A and B are straight forward: each bit corresponds to an 51 * output pin with the same order. Port C is different: bits 0...3 52 * correspond to bits 4...7 of the output register (PCDR). 53 */ 54 55 mask = comedi_dio_update_state(s, data); 56 if (mask) { 57 outb(PADR, CSCIR); 58 outb(s->state & 0xff, CSCDR); 59 60 outb(PBDR, CSCIR); 61 outb((s->state >> 8) & 0xff, CSCDR); 62 63 outb(PCDR, CSCIR); 64 val = inb(CSCDR) & 0x0f; 65 outb(((s->state >> 12) & 0xf0) | val, CSCDR); 66 } 67 68 outb(PADR, CSCIR); 69 val = inb(CSCDR); 70 outb(PBDR, CSCIR); 71 val |= (inb(CSCDR) << 8); 72 outb(PCDR, CSCIR); 73 val |= ((inb(CSCDR) & 0xf0) << 12); 74 75 data[1] = val; 76 77 return insn->n; 78 } 79 80 static int dnp_dio_insn_config(struct comedi_device *dev, 81 struct comedi_subdevice *s, 82 struct comedi_insn *insn, 83 unsigned int *data) 84 { 85 unsigned int chan = CR_CHAN(insn->chanspec); 86 unsigned int mask; 87 unsigned int val; 88 int ret; 89 90 ret = comedi_dio_insn_config(dev, s, insn, data, 0); 91 if (ret) 92 return ret; 93 94 if (chan < 8) { /* Port A */ 95 mask = 1 << chan; 96 outb(PAMR, CSCIR); 97 } else if (chan < 16) { /* Port B */ 98 mask = 1 << (chan - 8); 99 outb(PBMR, CSCIR); 100 } else { /* Port C */ 101 /* 102 * We have to pay attention with port C. 103 * This is the meaning of PCMR: 104 * Bit in PCMR: 7 6 5 4 3 2 1 0 105 * Corresponding port C pin: d 3 d 2 d 1 d 0 d= don't touch 106 * 107 * Multiplication by 2 brings bits into correct position 108 * for PCMR! 109 */ 110 mask = 1 << ((chan - 16) * 2); 111 outb(PCMR, CSCIR); 112 } 113 114 val = inb(CSCDR); 115 if (data[0] == COMEDI_OUTPUT) 116 val |= mask; 117 else 118 val &= ~mask; 119 outb(val, CSCDR); 120 121 return insn->n; 122 } 123 124 static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it) 125 { 126 struct comedi_subdevice *s; 127 int ret; 128 129 /* 130 * We use I/O ports 0x22, 0x23 and 0xa3-0xa9, which are always 131 * allocated for the primary 8259, so we don't need to allocate 132 * them ourselves. 133 */ 134 135 ret = comedi_alloc_subdevices(dev, 1); 136 if (ret) 137 return ret; 138 139 s = &dev->subdevices[0]; 140 /* digital i/o subdevice */ 141 s->type = COMEDI_SUBD_DIO; 142 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 143 s->n_chan = 20; 144 s->maxdata = 1; 145 s->range_table = &range_digital; 146 s->insn_bits = dnp_dio_insn_bits; 147 s->insn_config = dnp_dio_insn_config; 148 149 /* configure all ports as input (default) */ 150 outb(PAMR, CSCIR); 151 outb(0x00, CSCDR); 152 outb(PBMR, CSCIR); 153 outb(0x00, CSCDR); 154 outb(PCMR, CSCIR); 155 outb((inb(CSCDR) & 0xAA), CSCDR); 156 157 return 0; 158 } 159 160 static void dnp_detach(struct comedi_device *dev) 161 { 162 outb(PAMR, CSCIR); 163 outb(0x00, CSCDR); 164 outb(PBMR, CSCIR); 165 outb(0x00, CSCDR); 166 outb(PCMR, CSCIR); 167 outb((inb(CSCDR) & 0xAA), CSCDR); 168 } 169 170 static struct comedi_driver dnp_driver = { 171 .driver_name = "dnp-1486", 172 .module = THIS_MODULE, 173 .attach = dnp_attach, 174 .detach = dnp_detach, 175 }; 176 module_comedi_driver(dnp_driver); 177 178 MODULE_AUTHOR("Comedi https://www.comedi.org"); 179 MODULE_DESCRIPTION("Comedi low-level driver"); 180 MODULE_LICENSE("GPL"); 181