pcm3724.c revision 0707bb04be89b18ee83b5a997e36cc585f0b988d
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 68typedef struct { 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} boardtype; 75 76//used to track configured dios 77typedef struct { 78 int dio_1; 79 int dio_2; 80} priv_pcm3724; 81static const boardtype boardtypes[] = { 82 {"pcm3724", 48, 2, 0x00fc, PCM3724_SIZE,}, 83}; 84 85#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype)) 86#define this_board ((const boardtype *)dev->board_ptr) 87 88static struct comedi_driver driver_pcm3724 = { 89 driver_name:"pcm3724", 90 module:THIS_MODULE, 91 attach:pcm3724_attach, 92 detach:pcm3724_detach, 93 board_name:&boardtypes[0].name, 94 num_names:n_boardtypes, 95 offset:sizeof(boardtype), 96}; 97 98COMEDI_INITCLEANUP(driver_pcm3724); 99 100// (setq c-basic-offset 8) 101 102static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) 103{ 104 unsigned long iobase = arg; 105 unsigned char inbres; 106 //printk("8255cb %d %d %d %lx\n", dir,port,data,arg); 107 if (dir) { 108 //printk("8255 cb outb(%x, %lx)\n", data, iobase+port); 109 outb(data, iobase + port); 110 return 0; 111 } else { 112 inbres = inb(iobase + port); 113 //printk("8255 cb inb(%lx) = %x\n", iobase+port, inbres); 114 return inbres; 115 } 116} 117 118static int compute_buffer(int config, int devno, struct comedi_subdevice * s) 119{ 120 /* 1 in io_bits indicates output */ 121 if (s->io_bits & 0x0000ff) { 122 if (devno == 0) { 123 config |= BUF_A0; 124 } else { 125 config |= BUF_A1; 126 } 127 } 128 if (s->io_bits & 0x00ff00) { 129 if (devno == 0) { 130 config |= BUF_B0; 131 } else { 132 config |= BUF_B1; 133 } 134 } 135 if (s->io_bits & 0xff0000) { 136 if (devno == 0) { 137 config |= BUF_C0; 138 } else { 139 config |= BUF_C1; 140 } 141 } 142 return config; 143} 144 145static void do_3724_config(struct comedi_device * dev, struct comedi_subdevice * s, 146 int chanspec) 147{ 148 int config; 149 int buffer_config; 150 unsigned long port_8255_cfg; 151 152 config = CR_CW; 153 buffer_config = 0; 154 155 /* 1 in io_bits indicates output, 1 in config indicates input */ 156 if (!(s->io_bits & 0x0000ff)) { 157 config |= CR_A_IO; 158 } 159 if (!(s->io_bits & 0x00ff00)) { 160 config |= CR_B_IO; 161 } 162 if (!(s->io_bits & 0xff0000)) { 163 config |= CR_C_IO; 164 } 165 166 buffer_config = compute_buffer(0, 0, dev->subdevices); 167 buffer_config = compute_buffer(buffer_config, 1, (dev->subdevices) + 1); 168 169 if (s == dev->subdevices) { 170 port_8255_cfg = dev->iobase + _8255_CR; 171 } else { 172 port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR; 173 } 174 outb(buffer_config, dev->iobase + 8); /* update buffer register */ 175 //printk("pcm3724 buffer_config (%lx) %d, %x\n", dev->iobase + _8255_CR, chanspec, buffer_config); 176 outb(config, port_8255_cfg); 177} 178 179static void enable_chan(struct comedi_device * dev, struct comedi_subdevice * s, int chanspec) 180{ 181 unsigned int mask; 182 int gatecfg; 183 priv_pcm3724 *priv; 184 185 gatecfg = 0; 186 priv = (priv_pcm3724 *) (dev->private); 187 188 mask = 1 << CR_CHAN(chanspec); 189 if (s == dev->subdevices) { // subdev 0 190 priv->dio_1 |= mask; 191 } else { //subdev 1 192 priv->dio_2 |= mask; 193 } 194 if (priv->dio_1 & 0xff0000) { 195 gatecfg |= GATE_C0; 196 } 197 if (priv->dio_1 & 0xff00) { 198 gatecfg |= GATE_B0; 199 } 200 if (priv->dio_1 & 0xff) { 201 gatecfg |= GATE_A0; 202 } 203 if (priv->dio_2 & 0xff0000) { 204 gatecfg |= GATE_C1; 205 } 206 if (priv->dio_2 & 0xff00) { 207 gatecfg |= GATE_B1; 208 } 209 if (priv->dio_2 & 0xff) { 210 gatecfg |= GATE_A1; 211 } 212 // printk("gate control %x\n", gatecfg); 213 outb(gatecfg, dev->iobase + 9); 214} 215 216/* overriding the 8255 insn config */ 217static int subdev_3724_insn_config(struct comedi_device * dev, struct comedi_subdevice * s, 218 struct comedi_insn * insn, unsigned int * data) 219{ 220 unsigned int mask; 221 unsigned int bits; 222 223 mask = 1 << CR_CHAN(insn->chanspec); 224 if (mask & 0x0000ff) { 225 bits = 0x0000ff; 226 } else if (mask & 0x00ff00) { 227 bits = 0x00ff00; 228 } else if (mask & 0x0f0000) { 229 bits = 0x0f0000; 230 } else { 231 bits = 0xf00000; 232 } 233 234 switch (data[0]) { 235 case INSN_CONFIG_DIO_INPUT: 236 s->io_bits &= ~bits; 237 break; 238 case INSN_CONFIG_DIO_OUTPUT: 239 s->io_bits |= bits; 240 break; 241 case INSN_CONFIG_DIO_QUERY: 242 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; 243 return insn->n; 244 break; 245 default: 246 return -EINVAL; 247 } 248 249 do_3724_config(dev, s, insn->chanspec); 250 enable_chan(dev, s, insn->chanspec); 251 return 1; 252} 253 254static int pcm3724_attach(struct comedi_device * dev, struct comedi_devconfig * it) 255{ 256 unsigned long iobase; 257 unsigned int iorange; 258 int ret, i, n_subdevices; 259 260 iobase = it->options[0]; 261 iorange = this_board->io_range; 262 if ((ret = alloc_private(dev, sizeof(priv_pcm3724))) < 0) 263 return -ENOMEM; 264 265 ((priv_pcm3724 *) (dev->private))->dio_1 = 0; 266 ((priv_pcm3724 *) (dev->private))->dio_2 = 0; 267 268 printk("comedi%d: pcm3724: board=%s, 0x%03lx ", dev->minor, 269 this_board->name, iobase); 270 if (!iobase || !request_region(iobase, iorange, "pcm3724")) { 271 printk("I/O port conflict\n"); 272 return -EIO; 273 } 274 275 dev->iobase = iobase; 276 dev->board_name = this_board->name; 277 printk("\n"); 278 279 n_subdevices = this_board->numofports; 280 281 if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) 282 return ret; 283 284 for (i = 0; i < dev->n_subdevices; i++) { 285 subdev_8255_init(dev, dev->subdevices + i, subdev_8255_cb, 286 (unsigned long)(dev->iobase + SIZE_8255 * i)); 287 ((dev->subdevices) + i)->insn_config = subdev_3724_insn_config; 288 }; 289 return 0; 290} 291 292static int pcm3724_detach(struct comedi_device * dev) 293{ 294 int i; 295 296 if (dev->subdevices) { 297 for (i = 0; i < dev->n_subdevices; i++) { 298 subdev_8255_cleanup(dev, dev->subdevices + i); 299 } 300 } 301 if (dev->iobase) { 302 release_region(dev->iobase, this_board->io_range); 303 } 304 305 return 0; 306} 307