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