pcm3724.c revision c3744138715045adb316284ee7a1e608f0278f6c
1/* 2 comedi/drivers/pcm724.c 3 4 Drew Csillag <drew_csillag@yahoo.com> 5 6 hardware driver for Advantech card: 7 card: PCM-3724 8 driver: pcm3724 9 10 Options for PCM-3724 11 [0] - IO Base 12*/ 13/* 14Driver: pcm3724 15Description: Advantech PCM-3724 16Author: Drew Csillag <drew_csillag@yahoo.com> 17Devices: [Advantech] PCM-3724 (pcm724) 18Status: tested 19 20This is driver for digital I/O boards PCM-3724 with 48 DIO. 21It needs 8255.o for operations and only immediate mode is supported. 22See the source for configuration details. 23 24Copy/pasted/hacked from pcm724.c 25*/ 26/* 27 * check_driver overrides: 28 * struct comedi_insn 29 */ 30 31#include "../comedidev.h" 32 33#include <linux/ioport.h> 34#include <linux/delay.h> 35 36#include "8255.h" 37 38#define PCM3724_SIZE 16 39#define SIZE_8255 4 40 41#define BUF_C0 0x1 42#define BUF_B0 0x2 43#define BUF_A0 0x4 44#define BUF_C1 0x8 45#define BUF_B1 0x10 46#define BUF_A1 0x20 47 48#define GATE_A0 0x4 49#define GATE_B0 0x2 50#define GATE_C0 0x1 51#define GATE_A1 0x20 52#define GATE_B1 0x10 53#define GATE_C1 0x8 54 55/* from 8255.c */ 56#define CR_CW 0x80 57#define _8255_CR 3 58#define CR_B_IO 0x02 59#define CR_B_MODE 0x04 60#define CR_C_IO 0x09 61#define CR_A_IO 0x10 62#define CR_A_MODE(a) ((a)<<5) 63#define CR_CW 0x80 64 65static int pcm3724_attach(struct comedi_device *dev, struct comedi_devconfig *it); 66static int pcm3724_detach(struct comedi_device *dev); 67 68struct pcm3724_board { 69 const char *name; /* driver name */ 70 int dio; /* num of DIO */ 71 int numofports; /* num of 8255 subdevices */ 72 unsigned int IRQbits; /* allowed interrupts */ 73 unsigned int io_range; /* len of IO space */ 74}; 75 76/* used to track configured dios */ 77struct priv_pcm3724 { 78 int dio_1; 79 int dio_2; 80}; 81 82static const struct pcm3724_board boardtypes[] = { 83 {"pcm3724", 48, 2, 0x00fc, PCM3724_SIZE,}, 84}; 85 86#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcm3724_board)) 87#define this_board ((const struct pcm3724_board *)dev->board_ptr) 88 89static struct comedi_driver driver_pcm3724 = { 90 driver_name:"pcm3724", 91 module:THIS_MODULE, 92 attach:pcm3724_attach, 93 detach:pcm3724_detach, 94 board_name:&boardtypes[0].name, 95 num_names:n_boardtypes, 96 offset:sizeof(struct pcm3724_board), 97}; 98 99COMEDI_INITCLEANUP(driver_pcm3724); 100 101/* (setq c-basic-offset 8) */ 102 103static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) 104{ 105 unsigned long iobase = arg; 106 unsigned char inbres; 107 /* printk("8255cb %d %d %d %lx\n", dir,port,data,arg); */ 108 if (dir) { 109 /* printk("8255 cb outb(%x, %lx)\n", data, iobase+port); */ 110 outb(data, iobase + port); 111 return 0; 112 } else { 113 inbres = inb(iobase + port); 114 /* printk("8255 cb inb(%lx) = %x\n", iobase+port, inbres); */ 115 return inbres; 116 } 117} 118 119static int compute_buffer(int config, int devno, struct comedi_subdevice *s) 120{ 121 /* 1 in io_bits indicates output */ 122 if (s->io_bits & 0x0000ff) { 123 if (devno == 0) { 124 config |= BUF_A0; 125 } else { 126 config |= BUF_A1; 127 } 128 } 129 if (s->io_bits & 0x00ff00) { 130 if (devno == 0) { 131 config |= BUF_B0; 132 } else { 133 config |= BUF_B1; 134 } 135 } 136 if (s->io_bits & 0xff0000) { 137 if (devno == 0) { 138 config |= BUF_C0; 139 } else { 140 config |= BUF_C1; 141 } 142 } 143 return config; 144} 145 146static void do_3724_config(struct comedi_device *dev, struct comedi_subdevice *s, 147 int chanspec) 148{ 149 int config; 150 int buffer_config; 151 unsigned long port_8255_cfg; 152 153 config = CR_CW; 154 buffer_config = 0; 155 156 /* 1 in io_bits indicates output, 1 in config indicates input */ 157 if (!(s->io_bits & 0x0000ff)) { 158 config |= CR_A_IO; 159 } 160 if (!(s->io_bits & 0x00ff00)) { 161 config |= CR_B_IO; 162 } 163 if (!(s->io_bits & 0xff0000)) { 164 config |= CR_C_IO; 165 } 166 167 buffer_config = compute_buffer(0, 0, dev->subdevices); 168 buffer_config = compute_buffer(buffer_config, 1, (dev->subdevices) + 1); 169 170 if (s == dev->subdevices) { 171 port_8255_cfg = dev->iobase + _8255_CR; 172 } else { 173 port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR; 174 } 175 outb(buffer_config, dev->iobase + 8); /* update buffer register */ 176 /* printk("pcm3724 buffer_config (%lx) %d, %x\n", dev->iobase + _8255_CR, chanspec, buffer_config); */ 177 outb(config, port_8255_cfg); 178} 179 180static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s, int chanspec) 181{ 182 unsigned int mask; 183 int gatecfg; 184 struct priv_pcm3724 *priv; 185 186 gatecfg = 0; 187 priv = (struct priv_pcm3724 *) (dev->private); 188 189 mask = 1 << CR_CHAN(chanspec); 190 if (s == dev->subdevices) { /* subdev 0 */ 191 priv->dio_1 |= mask; 192 } else { /* subdev 1 */ 193 priv->dio_2 |= mask; 194 } 195 if (priv->dio_1 & 0xff0000) { 196 gatecfg |= GATE_C0; 197 } 198 if (priv->dio_1 & 0xff00) { 199 gatecfg |= GATE_B0; 200 } 201 if (priv->dio_1 & 0xff) { 202 gatecfg |= GATE_A0; 203 } 204 if (priv->dio_2 & 0xff0000) { 205 gatecfg |= GATE_C1; 206 } 207 if (priv->dio_2 & 0xff00) { 208 gatecfg |= GATE_B1; 209 } 210 if (priv->dio_2 & 0xff) { 211 gatecfg |= GATE_A1; 212 } 213 /* printk("gate control %x\n", gatecfg); */ 214 outb(gatecfg, dev->iobase + 9); 215} 216 217/* overriding the 8255 insn config */ 218static int subdev_3724_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 219 struct comedi_insn *insn, unsigned int *data) 220{ 221 unsigned int mask; 222 unsigned int bits; 223 224 mask = 1 << CR_CHAN(insn->chanspec); 225 if (mask & 0x0000ff) { 226 bits = 0x0000ff; 227 } else if (mask & 0x00ff00) { 228 bits = 0x00ff00; 229 } else if (mask & 0x0f0000) { 230 bits = 0x0f0000; 231 } else { 232 bits = 0xf00000; 233 } 234 235 switch (data[0]) { 236 case INSN_CONFIG_DIO_INPUT: 237 s->io_bits &= ~bits; 238 break; 239 case INSN_CONFIG_DIO_OUTPUT: 240 s->io_bits |= bits; 241 break; 242 case INSN_CONFIG_DIO_QUERY: 243 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; 244 return insn->n; 245 break; 246 default: 247 return -EINVAL; 248 } 249 250 do_3724_config(dev, s, insn->chanspec); 251 enable_chan(dev, s, insn->chanspec); 252 return 1; 253} 254 255static int pcm3724_attach(struct comedi_device *dev, struct comedi_devconfig *it) 256{ 257 unsigned long iobase; 258 unsigned int iorange; 259 int ret, i, n_subdevices; 260 261 iobase = it->options[0]; 262 iorange = this_board->io_range; 263 264 ret = alloc_private(dev, sizeof(struct priv_pcm3724)); 265 if (ret < 0) 266 return -ENOMEM; 267 268 ((struct priv_pcm3724 *) (dev->private))->dio_1 = 0; 269 ((struct priv_pcm3724 *) (dev->private))->dio_2 = 0; 270 271 printk("comedi%d: pcm3724: board=%s, 0x%03lx ", dev->minor, 272 this_board->name, iobase); 273 if (!iobase || !request_region(iobase, iorange, "pcm3724")) { 274 printk("I/O port conflict\n"); 275 return -EIO; 276 } 277 278 dev->iobase = iobase; 279 dev->board_name = this_board->name; 280 printk("\n"); 281 282 n_subdevices = this_board->numofports; 283 284 ret = alloc_subdevices(dev, n_subdevices); 285 if (ret < 0) 286 return ret; 287 288 for (i = 0; i < dev->n_subdevices; i++) { 289 subdev_8255_init(dev, dev->subdevices + i, subdev_8255_cb, 290 (unsigned long)(dev->iobase + SIZE_8255 * i)); 291 ((dev->subdevices) + i)->insn_config = subdev_3724_insn_config; 292 }; 293 return 0; 294} 295 296static int pcm3724_detach(struct comedi_device *dev) 297{ 298 int i; 299 300 if (dev->subdevices) { 301 for (i = 0; i < dev->n_subdevices; i++) { 302 subdev_8255_cleanup(dev, dev->subdevices + i); 303 } 304 } 305 if (dev->iobase) { 306 release_region(dev->iobase, this_board->io_range); 307 } 308 309 return 0; 310} 311