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