c6xdigio.c revision 90f703d30dd3e0c16ff80f35e34e511385a05ad5
1/* 2 comedi/drivers/c6xdigio.c 3 4 Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card. 5 (http://robot0.ge.uiuc.edu/~spong/mecha/) 6 7 COMEDI - Linux Control and Measurement Device Interface 8 Copyright (C) 1999 Dan Block 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 24 */ 25/* 26Driver: c6xdigio 27Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card 28Author: Dan Block 29Status: unknown 30Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio) 31Updated: Sun Nov 20 20:18:34 EST 2005 32 33This driver will not work with a 2.4 kernel. 34http://robot0.ge.uiuc.edu/~spong/mecha/ 35 36*/ 37 38#include <linux/kernel.h> 39#include <linux/module.h> 40#include <linux/sched.h> 41#include <linux/mm.h> 42#include <linux/errno.h> 43#include <linux/ioport.h> 44#include <linux/delay.h> 45#include <linux/interrupt.h> 46#include <linux/timex.h> 47#include <linux/timer.h> 48#include <linux/io.h> 49#include <linux/pnp.h> 50 51#include "../comedidev.h" 52 53static u8 ReadByteFromHwPort(unsigned long addr) 54{ 55 u8 result = inb(addr); 56 return result; 57} 58 59static void WriteByteToHwPort(unsigned long addr, u8 val) 60{ 61 outb_p(val, addr); 62} 63 64#define C6XDIGIO_SIZE 3 65 66/* 67 * port offsets 68 */ 69#define C6XDIGIO_PARALLEL_DATA 0 70#define C6XDIGIO_PARALLEL_STATUS 1 71#define C6XDIGIO_PARALLEL_CONTROL 2 72struct pwmbitstype { 73 unsigned sb0:2; 74 unsigned sb1:2; 75 unsigned sb2:2; 76 unsigned sb3:2; 77 unsigned sb4:2; 78}; 79union pwmcmdtype { 80 unsigned cmd; /* assuming here that int is 32bit */ 81 struct pwmbitstype bits; 82}; 83struct encbitstype { 84 unsigned sb0:3; 85 unsigned sb1:3; 86 unsigned sb2:3; 87 unsigned sb3:3; 88 unsigned sb4:3; 89 unsigned sb5:3; 90 unsigned sb6:3; 91 unsigned sb7:3; 92}; 93union encvaluetype { 94 unsigned value; 95 struct encbitstype bits; 96}; 97 98#define C6XDIGIO_TIME_OUT 20 99 100static int c6xdigio_attach(struct comedi_device *dev, 101 struct comedi_devconfig *it); 102static int c6xdigio_detach(struct comedi_device *dev); 103struct comedi_driver driver_c6xdigio = { 104 .driver_name = "c6xdigio", 105 .module = THIS_MODULE, 106 .attach = c6xdigio_attach, 107 .detach = c6xdigio_detach, 108}; 109 110static void C6X_pwmInit(unsigned long baseAddr) 111{ 112 int timeout = 0; 113 114/* printk("Inside C6X_pwmInit\n"); */ 115 116 WriteByteToHwPort(baseAddr, 0x70); 117 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0) 118 && (timeout < C6XDIGIO_TIME_OUT)) { 119 timeout++; 120 } 121 122 WriteByteToHwPort(baseAddr, 0x74); 123 timeout = 0; 124 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80) 125 && (timeout < C6XDIGIO_TIME_OUT)) { 126 timeout++; 127 } 128 129 WriteByteToHwPort(baseAddr, 0x70); 130 timeout = 0; 131 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0) 132 && (timeout < C6XDIGIO_TIME_OUT)) { 133 timeout++; 134 } 135 136 WriteByteToHwPort(baseAddr, 0x0); 137 timeout = 0; 138 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80) 139 && (timeout < C6XDIGIO_TIME_OUT)) { 140 timeout++; 141 } 142 143} 144 145static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value) 146{ 147 unsigned ppcmd; 148 union pwmcmdtype pwm; 149 int timeout = 0; 150 unsigned tmp; 151 152 /* printk("Inside C6X_pwmOutput\n"); */ 153 154 pwm.cmd = value; 155 if (pwm.cmd > 498) 156 pwm.cmd = 498; 157 if (pwm.cmd < 2) 158 pwm.cmd = 2; 159 160 if (channel == 0) { 161 ppcmd = 0x28; 162 } else { /* if channel == 1 */ 163 ppcmd = 0x30; 164 } /* endif */ 165 166 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0); 167 tmp = ReadByteFromHwPort(baseAddr + 1); 168 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 169 tmp = ReadByteFromHwPort(baseAddr + 1); 170 timeout++; 171 } 172 173 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4); 174 timeout = 0; 175 tmp = ReadByteFromHwPort(baseAddr + 1); 176 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 177 tmp = ReadByteFromHwPort(baseAddr + 1); 178 timeout++; 179 } 180 181 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2); 182 tmp = ReadByteFromHwPort(baseAddr + 1); 183 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 184 tmp = ReadByteFromHwPort(baseAddr + 1); 185 timeout++; 186 } 187 188 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4); 189 timeout = 0; 190 tmp = ReadByteFromHwPort(baseAddr + 1); 191 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 192 tmp = ReadByteFromHwPort(baseAddr + 1); 193 timeout++; 194 } 195 196 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4); 197 tmp = ReadByteFromHwPort(baseAddr + 1); 198 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 199 tmp = ReadByteFromHwPort(baseAddr + 1); 200 timeout++; 201 } 202 203 WriteByteToHwPort(baseAddr, 0x0); 204 timeout = 0; 205 tmp = ReadByteFromHwPort(baseAddr + 1); 206 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 207 tmp = ReadByteFromHwPort(baseAddr + 1); 208 timeout++; 209 } 210 211} 212 213static int C6X_encInput(unsigned long baseAddr, unsigned channel) 214{ 215 unsigned ppcmd; 216 union encvaluetype enc; 217 int timeout = 0; 218 int tmp; 219 220 /* printk("Inside C6X_encInput\n"); */ 221 222 enc.value = 0; 223 if (channel == 0) 224 ppcmd = 0x48; 225 else 226 ppcmd = 0x50; 227 228 WriteByteToHwPort(baseAddr, ppcmd); 229 tmp = ReadByteFromHwPort(baseAddr + 1); 230 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 231 tmp = ReadByteFromHwPort(baseAddr + 1); 232 timeout++; 233 } 234 235 enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 236 WriteByteToHwPort(baseAddr, ppcmd + 0x4); 237 timeout = 0; 238 tmp = ReadByteFromHwPort(baseAddr + 1); 239 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 240 tmp = ReadByteFromHwPort(baseAddr + 1); 241 timeout++; 242 } 243 enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 244 WriteByteToHwPort(baseAddr, ppcmd); 245 timeout = 0; 246 tmp = ReadByteFromHwPort(baseAddr + 1); 247 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 248 tmp = ReadByteFromHwPort(baseAddr + 1); 249 timeout++; 250 } 251 enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 252 WriteByteToHwPort(baseAddr, ppcmd + 0x4); 253 timeout = 0; 254 tmp = ReadByteFromHwPort(baseAddr + 1); 255 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 256 tmp = ReadByteFromHwPort(baseAddr + 1); 257 timeout++; 258 } 259 enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 260 WriteByteToHwPort(baseAddr, ppcmd); 261 timeout = 0; 262 tmp = ReadByteFromHwPort(baseAddr + 1); 263 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 264 tmp = ReadByteFromHwPort(baseAddr + 1); 265 timeout++; 266 } 267 enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 268 WriteByteToHwPort(baseAddr, ppcmd + 0x4); 269 timeout = 0; 270 tmp = ReadByteFromHwPort(baseAddr + 1); 271 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 272 tmp = ReadByteFromHwPort(baseAddr + 1); 273 timeout++; 274 } 275 enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 276 WriteByteToHwPort(baseAddr, ppcmd); 277 timeout = 0; 278 tmp = ReadByteFromHwPort(baseAddr + 1); 279 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) { 280 tmp = ReadByteFromHwPort(baseAddr + 1); 281 timeout++; 282 } 283 enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 284 WriteByteToHwPort(baseAddr, ppcmd + 0x4); 285 timeout = 0; 286 tmp = ReadByteFromHwPort(baseAddr + 1); 287 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 288 tmp = ReadByteFromHwPort(baseAddr + 1); 289 timeout++; 290 } 291 enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 292 WriteByteToHwPort(baseAddr, ppcmd); 293 timeout = 0; 294 tmp = ReadByteFromHwPort(baseAddr + 1); 295 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) { 296 tmp = ReadByteFromHwPort(baseAddr + 1); 297 timeout++; 298 } 299 300 WriteByteToHwPort(baseAddr, 0x0); 301 timeout = 0; 302 tmp = ReadByteFromHwPort(baseAddr + 1); 303 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 304 tmp = ReadByteFromHwPort(baseAddr + 1); 305 timeout++; 306 } 307 308 return enc.value ^ 0x800000; 309} 310 311static void C6X_encResetAll(unsigned long baseAddr) 312{ 313 unsigned timeout = 0; 314 315/* printk("Inside C6X_encResetAll\n"); */ 316 317 WriteByteToHwPort(baseAddr, 0x68); 318 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0) 319 && (timeout < C6XDIGIO_TIME_OUT)) { 320 timeout++; 321 } 322 WriteByteToHwPort(baseAddr, 0x6C); 323 timeout = 0; 324 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80) 325 && (timeout < C6XDIGIO_TIME_OUT)) { 326 timeout++; 327 } 328 WriteByteToHwPort(baseAddr, 0x68); 329 timeout = 0; 330 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0) 331 && (timeout < C6XDIGIO_TIME_OUT)) { 332 timeout++; 333 } 334 WriteByteToHwPort(baseAddr, 0x0); 335 timeout = 0; 336 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80) 337 && (timeout < C6XDIGIO_TIME_OUT)) { 338 timeout++; 339 } 340} 341 342static int c6xdigio_pwmo_insn_read(struct comedi_device *dev, 343 struct comedi_subdevice *s, 344 struct comedi_insn *insn, unsigned int *data) 345{ 346 printk("c6xdigio_pwmo_insn_read %x\n", insn->n); 347 return insn->n; 348} 349 350static int c6xdigio_pwmo_insn_write(struct comedi_device *dev, 351 struct comedi_subdevice *s, 352 struct comedi_insn *insn, 353 unsigned int *data) 354{ 355 int i; 356 int chan = CR_CHAN(insn->chanspec); 357 358 /* printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */ 359 for (i = 0; i < insn->n; i++) { 360 C6X_pwmOutput(dev->iobase, chan, data[i]); 361 /* devpriv->ao_readback[chan] = data[i]; */ 362 } 363 return i; 364} 365 366/* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */ 367/* struct comedi_subdevice *s, */ 368/* struct comedi_insn *insn, */ 369/* unsigned int *data) */ 370/* { */ 371/* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */ 372/* return insn->n; */ 373/* } */ 374 375/* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */ 376/* struct comedi_subdevice *s, */ 377/* struct comedi_insn *insn, */ 378/* unsigned int *data) */ 379/* { */ 380/* int i; */ 381/* int chan = CR_CHAN(insn->chanspec); */ 382 /* *//* C6X_encResetAll( dev->iobase ); */ 383 /* *//* return insn->n; */ 384/* } */ 385 386static int c6xdigio_ei_insn_read(struct comedi_device *dev, 387 struct comedi_subdevice *s, 388 struct comedi_insn *insn, unsigned int *data) 389{ 390 /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */ 391 int n; 392 int chan = CR_CHAN(insn->chanspec); 393 394 for (n = 0; n < insn->n; n++) 395 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff); 396 397 return n; 398} 399 400static void board_init(struct comedi_device *dev) 401{ 402 403 /* printk("Inside board_init\n"); */ 404 405 C6X_pwmInit(dev->iobase); 406 C6X_encResetAll(dev->iobase); 407 408} 409 410/* static void board_halt(struct comedi_device *dev) { */ 411/* C6X_pwmInit(dev->iobase); */ 412/* } */ 413 414/* 415 options[0] - I/O port 416 options[1] - irq 417 options[2] - number of encoder chips installed 418 */ 419 420static const struct pnp_device_id c6xdigio_pnp_tbl[] = { 421 /* Standard LPT Printer Port */ 422 {.id = "PNP0400", .driver_data = 0}, 423 /* ECP Printer Port */ 424 {.id = "PNP0401", .driver_data = 0}, 425 {} 426}; 427 428static struct pnp_driver c6xdigio_pnp_driver = { 429 .name = "c6xdigio", 430 .id_table = c6xdigio_pnp_tbl, 431}; 432 433static int c6xdigio_attach(struct comedi_device *dev, 434 struct comedi_devconfig *it) 435{ 436 int result = 0; 437 unsigned long iobase; 438 unsigned int irq; 439 struct comedi_subdevice *s; 440 441 iobase = it->options[0]; 442 printk("comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase); 443 if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) { 444 printk("comedi%d: I/O port conflict\n", dev->minor); 445 return -EIO; 446 } 447 dev->iobase = iobase; 448 dev->board_name = "c6xdigio"; 449 450 result = alloc_subdevices(dev, 2); /* 3 with encoder_init write */ 451 if (result < 0) 452 return result; 453 454 /* Make sure that PnP ports get activated */ 455 pnp_register_driver(&c6xdigio_pnp_driver); 456 457 irq = it->options[1]; 458 if (irq > 0) 459 printk("comedi%d: irq = %u ignored\n", dev->minor, irq); 460 else if (irq == 0) 461 printk("comedi%d: no irq\n", dev->minor); 462 463 s = dev->subdevices + 0; 464 /* pwm output subdevice */ 465 s->type = COMEDI_SUBD_AO; /* Not sure what to put here */ 466 s->subdev_flags = SDF_WRITEABLE; 467 s->n_chan = 2; 468 /* s->trig[0] = c6xdigio_pwmo; */ 469 s->insn_read = c6xdigio_pwmo_insn_read; 470 s->insn_write = c6xdigio_pwmo_insn_write; 471 s->maxdata = 500; 472 s->range_table = &range_bipolar10; /* A suitable lie */ 473 474 s = dev->subdevices + 1; 475 /* encoder (counter) subdevice */ 476 s->type = COMEDI_SUBD_COUNTER; 477 s->subdev_flags = SDF_READABLE | SDF_LSAMPL; 478 s->n_chan = 2; 479 /* s->trig[0] = c6xdigio_ei; */ 480 s->insn_read = c6xdigio_ei_insn_read; 481 s->maxdata = 0xffffff; 482 s->range_table = &range_unknown; 483 484 /* s = dev->subdevices + 2; */ 485 /* pwm output subdevice */ 486 /* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */ 487 /* s->subdev_flags = SDF_WRITEABLE; */ 488 /* s->n_chan = 1; */ 489 /* s->trig[0] = c6xdigio_ei_init; */ 490 /* s->insn_read = c6xdigio_ei_init_insn_read; */ 491 /* s->insn_write = c6xdigio_ei_init_insn_write; */ 492 /* s->maxdata = 0xFFFF; // Really just a don't care */ 493 /* s->range_table = &range_unknown; // Not sure what to put here */ 494 495 /* I will call this init anyway but more than likely the DSP board */ 496 /* will not be connected when device driver is loaded. */ 497 board_init(dev); 498 499 return 0; 500} 501 502static int c6xdigio_detach(struct comedi_device *dev) 503{ 504 /* board_halt(dev); may not need this */ 505 506 printk("comedi%d: c6xdigio: remove\n", dev->minor); 507 508 if (dev->iobase) 509 release_region(dev->iobase, C6XDIGIO_SIZE); 510 511 /* Not using IRQ so I am not sure if I need this */ 512 if (dev->irq) 513 free_irq(dev->irq, dev); 514 515 pnp_unregister_driver(&c6xdigio_pnp_driver); 516 517 return 0; 518} 519 520COMEDI_INITCLEANUP(driver_c6xdigio); 521 522MODULE_AUTHOR("Comedi http://www.comedi.org"); 523MODULE_DESCRIPTION("Comedi low-level driver"); 524MODULE_LICENSE("GPL"); 525