1/* 2 comedi/drivers/das16cs.c 3 Driver for Computer Boards PC-CARD DAS16/16. 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 PCMCIA support code for this driver is adapted from the dummy_cs.c 19 driver of the Linux PCMCIA Card Services package. 20 21 The initial developer of the original code is David A. Hinds 22 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds 23 are Copyright (C) 1999 David A. Hinds. All Rights Reserved. 24 25*/ 26/* 27Driver: cb_das16_cs 28Description: Computer Boards PC-CARD DAS16/16 29Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO 30Author: ds 31Updated: Mon, 04 Nov 2002 20:04:21 -0800 32Status: experimental 33 34 35*/ 36 37#include <linux/module.h> 38#include <linux/interrupt.h> 39#include <linux/delay.h> 40 41#include "../comedidev.h" 42 43#include <pcmcia/cistpl.h> 44#include <pcmcia/ds.h> 45 46#include "comedi_fc.h" 47#include "8253.h" 48 49#define DAS16CS_ADC_DATA 0 50#define DAS16CS_DIO_MUX 2 51#define DAS16CS_MISC1 4 52#define DAS16CS_MISC2 6 53#define DAS16CS_CTR0 8 54#define DAS16CS_CTR1 10 55#define DAS16CS_CTR2 12 56#define DAS16CS_CTR_CONTROL 14 57#define DAS16CS_DIO 16 58 59struct das16cs_board { 60 const char *name; 61 int device_id; 62 int n_ao_chans; 63}; 64 65static const struct das16cs_board das16cs_boards[] = { 66 { 67 .name = "PC-CARD DAS16/16-AO", 68 .device_id = 0x0039, 69 .n_ao_chans = 2, 70 }, { 71 .name = "PCM-DAS16s/16", 72 .device_id = 0x4009, 73 .n_ao_chans = 0, 74 }, { 75 .name = "PC-CARD DAS16/16", 76 .device_id = 0x0000, /* unknown */ 77 .n_ao_chans = 0, 78 }, 79}; 80 81struct das16cs_private { 82 unsigned short status1; 83 unsigned short status2; 84}; 85 86static const struct comedi_lrange das16cs_ai_range = { 87 4, { 88 BIP_RANGE(10), 89 BIP_RANGE(5), 90 BIP_RANGE(2.5), 91 BIP_RANGE(1.25), 92 } 93}; 94 95static int das16cs_ai_eoc(struct comedi_device *dev, 96 struct comedi_subdevice *s, 97 struct comedi_insn *insn, 98 unsigned long context) 99{ 100 unsigned int status; 101 102 status = inw(dev->iobase + DAS16CS_MISC1); 103 if (status & 0x0080) 104 return 0; 105 return -EBUSY; 106} 107 108static int das16cs_ai_rinsn(struct comedi_device *dev, 109 struct comedi_subdevice *s, 110 struct comedi_insn *insn, unsigned int *data) 111{ 112 struct das16cs_private *devpriv = dev->private; 113 int chan = CR_CHAN(insn->chanspec); 114 int range = CR_RANGE(insn->chanspec); 115 int aref = CR_AREF(insn->chanspec); 116 int ret; 117 int i; 118 119 outw(chan, dev->iobase + DAS16CS_DIO_MUX); 120 121 devpriv->status1 &= ~0xf320; 122 devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020; 123 outw(devpriv->status1, dev->iobase + DAS16CS_MISC1); 124 125 devpriv->status2 &= ~0xff00; 126 switch (range) { 127 case 0: 128 devpriv->status2 |= 0x800; 129 break; 130 case 1: 131 devpriv->status2 |= 0x000; 132 break; 133 case 2: 134 devpriv->status2 |= 0x100; 135 break; 136 case 3: 137 devpriv->status2 |= 0x200; 138 break; 139 } 140 outw(devpriv->status2, dev->iobase + DAS16CS_MISC2); 141 142 for (i = 0; i < insn->n; i++) { 143 outw(0, dev->iobase + DAS16CS_ADC_DATA); 144 145 ret = comedi_timeout(dev, s, insn, das16cs_ai_eoc, 0); 146 if (ret) 147 return ret; 148 149 data[i] = inw(dev->iobase + DAS16CS_ADC_DATA); 150 } 151 152 return i; 153} 154 155static int das16cs_ao_insn_write(struct comedi_device *dev, 156 struct comedi_subdevice *s, 157 struct comedi_insn *insn, 158 unsigned int *data) 159{ 160 struct das16cs_private *devpriv = dev->private; 161 unsigned int chan = CR_CHAN(insn->chanspec); 162 unsigned int val = s->readback[chan]; 163 unsigned short status1; 164 int bit; 165 int i; 166 167 for (i = 0; i < insn->n; i++) { 168 val = data[i]; 169 170 outw(devpriv->status1, dev->iobase + DAS16CS_MISC1); 171 udelay(1); 172 173 status1 = devpriv->status1 & ~0xf; 174 if (chan) 175 status1 |= 0x0001; 176 else 177 status1 |= 0x0008; 178 179 outw(status1, dev->iobase + DAS16CS_MISC1); 180 udelay(1); 181 182 for (bit = 15; bit >= 0; bit--) { 183 int b = (val >> bit) & 0x1; 184 185 b <<= 1; 186 outw(status1 | b | 0x0000, dev->iobase + DAS16CS_MISC1); 187 udelay(1); 188 outw(status1 | b | 0x0004, dev->iobase + DAS16CS_MISC1); 189 udelay(1); 190 } 191 /* 192 * Make both DAC0CS and DAC1CS high to load 193 * the new data and update analog the output 194 */ 195 outw(status1 | 0x9, dev->iobase + DAS16CS_MISC1); 196 } 197 s->readback[chan] = val; 198 199 return insn->n; 200} 201 202static int das16cs_dio_insn_bits(struct comedi_device *dev, 203 struct comedi_subdevice *s, 204 struct comedi_insn *insn, 205 unsigned int *data) 206{ 207 if (comedi_dio_update_state(s, data)) 208 outw(s->state, dev->iobase + DAS16CS_DIO); 209 210 data[1] = inw(dev->iobase + DAS16CS_DIO); 211 212 return insn->n; 213} 214 215static int das16cs_dio_insn_config(struct comedi_device *dev, 216 struct comedi_subdevice *s, 217 struct comedi_insn *insn, 218 unsigned int *data) 219{ 220 struct das16cs_private *devpriv = dev->private; 221 unsigned int chan = CR_CHAN(insn->chanspec); 222 unsigned int mask; 223 int ret; 224 225 if (chan < 4) 226 mask = 0x0f; 227 else 228 mask = 0xf0; 229 230 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 231 if (ret) 232 return ret; 233 234 devpriv->status2 &= ~0x00c0; 235 devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0; 236 devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0; 237 238 outw(devpriv->status2, dev->iobase + DAS16CS_MISC2); 239 240 return insn->n; 241} 242 243static const void *das16cs_find_boardinfo(struct comedi_device *dev, 244 struct pcmcia_device *link) 245{ 246 const struct das16cs_board *board; 247 int i; 248 249 for (i = 0; i < ARRAY_SIZE(das16cs_boards); i++) { 250 board = &das16cs_boards[i]; 251 if (board->device_id == link->card_id) 252 return board; 253 } 254 255 return NULL; 256} 257 258static int das16cs_auto_attach(struct comedi_device *dev, 259 unsigned long context) 260{ 261 struct pcmcia_device *link = comedi_to_pcmcia_dev(dev); 262 const struct das16cs_board *board; 263 struct das16cs_private *devpriv; 264 struct comedi_subdevice *s; 265 int ret; 266 267 board = das16cs_find_boardinfo(dev, link); 268 if (!board) 269 return -ENODEV; 270 dev->board_ptr = board; 271 dev->board_name = board->name; 272 273 link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; 274 ret = comedi_pcmcia_enable(dev, NULL); 275 if (ret) 276 return ret; 277 dev->iobase = link->resource[0]->start; 278 279 link->priv = dev; 280 281 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 282 if (!devpriv) 283 return -ENOMEM; 284 285 ret = comedi_alloc_subdevices(dev, 3); 286 if (ret) 287 return ret; 288 289 s = &dev->subdevices[0]; 290 /* analog input subdevice */ 291 s->type = COMEDI_SUBD_AI; 292 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; 293 s->n_chan = 16; 294 s->maxdata = 0xffff; 295 s->range_table = &das16cs_ai_range; 296 s->len_chanlist = 16; 297 s->insn_read = das16cs_ai_rinsn; 298 299 s = &dev->subdevices[1]; 300 /* analog output subdevice */ 301 if (board->n_ao_chans) { 302 s->type = COMEDI_SUBD_AO; 303 s->subdev_flags = SDF_WRITABLE; 304 s->n_chan = board->n_ao_chans; 305 s->maxdata = 0xffff; 306 s->range_table = &range_bipolar10; 307 s->insn_write = &das16cs_ao_insn_write; 308 s->insn_read = comedi_readback_insn_read; 309 310 ret = comedi_alloc_subdev_readback(s); 311 if (ret) 312 return ret; 313 } else { 314 s->type = COMEDI_SUBD_UNUSED; 315 } 316 317 s = &dev->subdevices[2]; 318 /* digital i/o subdevice */ 319 s->type = COMEDI_SUBD_DIO; 320 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 321 s->n_chan = 8; 322 s->maxdata = 1; 323 s->range_table = &range_digital; 324 s->insn_bits = das16cs_dio_insn_bits; 325 s->insn_config = das16cs_dio_insn_config; 326 327 return 0; 328} 329 330static struct comedi_driver driver_das16cs = { 331 .driver_name = "cb_das16_cs", 332 .module = THIS_MODULE, 333 .auto_attach = das16cs_auto_attach, 334 .detach = comedi_pcmcia_disable, 335}; 336 337static int das16cs_pcmcia_attach(struct pcmcia_device *link) 338{ 339 return comedi_pcmcia_auto_config(link, &driver_das16cs); 340} 341 342static const struct pcmcia_device_id das16cs_id_table[] = { 343 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039), 344 PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009), 345 PCMCIA_DEVICE_NULL 346}; 347MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table); 348 349static struct pcmcia_driver das16cs_driver = { 350 .name = "cb_das16_cs", 351 .owner = THIS_MODULE, 352 .id_table = das16cs_id_table, 353 .probe = das16cs_pcmcia_attach, 354 .remove = comedi_pcmcia_auto_unconfig, 355}; 356module_comedi_pcmcia_driver(driver_das16cs, das16cs_driver); 357 358MODULE_AUTHOR("David A. Schleef <ds@schleef.org>"); 359MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16"); 360MODULE_LICENSE("GPL"); 361