c6xdigio.c revision f7cbd7aad063b2a4b7aff6a743b2b00015ce3c3e
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 <asm/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, struct comedi_devconfig *it); 101static int c6xdigio_detach(struct comedi_device *dev); 102struct comedi_driver driver_c6xdigio = { 103 driver_name:"c6xdigio", 104 module:THIS_MODULE, 105 attach:c6xdigio_attach, 106 detach:c6xdigio_detach, 107}; 108 109static void C6X_pwmInit(unsigned long baseAddr) 110{ 111 int timeout = 0; 112 113/* printk("Inside C6X_pwmInit\n"); */ 114 115 WriteByteToHwPort(baseAddr, 0x70); 116 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0) 117 && (timeout < C6XDIGIO_TIME_OUT)) { 118 timeout++; 119 } 120 121 WriteByteToHwPort(baseAddr, 0x74); 122 timeout = 0; 123 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80) 124 && (timeout < C6XDIGIO_TIME_OUT)) { 125 timeout++; 126 } 127 128 WriteByteToHwPort(baseAddr, 0x70); 129 timeout = 0; 130 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0) 131 && (timeout < C6XDIGIO_TIME_OUT)) { 132 timeout++; 133 } 134 135 WriteByteToHwPort(baseAddr, 0x0); 136 timeout = 0; 137 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80) 138 && (timeout < C6XDIGIO_TIME_OUT)) { 139 timeout++; 140 } 141 142} 143 144static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value) 145{ 146 unsigned ppcmd; 147 union pwmcmdtype pwm; 148 int timeout = 0; 149 unsigned tmp; 150 151 /* printk("Inside C6X_pwmOutput\n"); */ 152 153 pwm.cmd = value; 154 if (pwm.cmd > 498) 155 pwm.cmd = 498; 156 if (pwm.cmd < 2) 157 pwm.cmd = 2; 158 159 if (channel == 0) { 160 ppcmd = 0x28; 161 } else { /* if channel == 1 */ 162 ppcmd = 0x30; 163 } /* endif */ 164 165 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0); 166 tmp = ReadByteFromHwPort(baseAddr + 1); 167 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 168 tmp = ReadByteFromHwPort(baseAddr + 1); 169 timeout++; 170 } 171 172 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4); 173 timeout = 0; 174 tmp = ReadByteFromHwPort(baseAddr + 1); 175 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 176 tmp = ReadByteFromHwPort(baseAddr + 1); 177 timeout++; 178 } 179 180 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2); 181 tmp = ReadByteFromHwPort(baseAddr + 1); 182 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 183 tmp = ReadByteFromHwPort(baseAddr + 1); 184 timeout++; 185 } 186 187 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4); 188 timeout = 0; 189 tmp = ReadByteFromHwPort(baseAddr + 1); 190 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 191 tmp = ReadByteFromHwPort(baseAddr + 1); 192 timeout++; 193 } 194 195 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4); 196 tmp = ReadByteFromHwPort(baseAddr + 1); 197 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 198 tmp = ReadByteFromHwPort(baseAddr + 1); 199 timeout++; 200 } 201 202 WriteByteToHwPort(baseAddr, 0x0); 203 timeout = 0; 204 tmp = ReadByteFromHwPort(baseAddr + 1); 205 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 206 tmp = ReadByteFromHwPort(baseAddr + 1); 207 timeout++; 208 } 209 210} 211 212static int C6X_encInput(unsigned long baseAddr, unsigned channel) 213{ 214 unsigned ppcmd; 215 union encvaluetype enc; 216 int timeout = 0; 217 int tmp; 218 219 /* printk("Inside C6X_encInput\n"); */ 220 221 enc.value = 0; 222 if (channel == 0) { 223 ppcmd = 0x48; 224 } else { 225 ppcmd = 0x50; 226 } 227 WriteByteToHwPort(baseAddr, ppcmd); 228 tmp = ReadByteFromHwPort(baseAddr + 1); 229 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 230 tmp = ReadByteFromHwPort(baseAddr + 1); 231 timeout++; 232 } 233 234 enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 235 WriteByteToHwPort(baseAddr, ppcmd + 0x4); 236 timeout = 0; 237 tmp = ReadByteFromHwPort(baseAddr + 1); 238 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 239 tmp = ReadByteFromHwPort(baseAddr + 1); 240 timeout++; 241 } 242 enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 243 WriteByteToHwPort(baseAddr, ppcmd); 244 timeout = 0; 245 tmp = ReadByteFromHwPort(baseAddr + 1); 246 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 247 tmp = ReadByteFromHwPort(baseAddr + 1); 248 timeout++; 249 } 250 enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 251 WriteByteToHwPort(baseAddr, ppcmd + 0x4); 252 timeout = 0; 253 tmp = ReadByteFromHwPort(baseAddr + 1); 254 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 255 tmp = ReadByteFromHwPort(baseAddr + 1); 256 timeout++; 257 } 258 enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 259 WriteByteToHwPort(baseAddr, ppcmd); 260 timeout = 0; 261 tmp = ReadByteFromHwPort(baseAddr + 1); 262 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) { 263 tmp = ReadByteFromHwPort(baseAddr + 1); 264 timeout++; 265 } 266 enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 267 WriteByteToHwPort(baseAddr, ppcmd + 0x4); 268 timeout = 0; 269 tmp = ReadByteFromHwPort(baseAddr + 1); 270 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 271 tmp = ReadByteFromHwPort(baseAddr + 1); 272 timeout++; 273 } 274 enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 275 WriteByteToHwPort(baseAddr, ppcmd); 276 timeout = 0; 277 tmp = ReadByteFromHwPort(baseAddr + 1); 278 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) { 279 tmp = ReadByteFromHwPort(baseAddr + 1); 280 timeout++; 281 } 282 enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 283 WriteByteToHwPort(baseAddr, ppcmd + 0x4); 284 timeout = 0; 285 tmp = ReadByteFromHwPort(baseAddr + 1); 286 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 287 tmp = ReadByteFromHwPort(baseAddr + 1); 288 timeout++; 289 } 290 enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7); 291 WriteByteToHwPort(baseAddr, ppcmd); 292 timeout = 0; 293 tmp = ReadByteFromHwPort(baseAddr + 1); 294 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) { 295 tmp = ReadByteFromHwPort(baseAddr + 1); 296 timeout++; 297 } 298 299 WriteByteToHwPort(baseAddr, 0x0); 300 timeout = 0; 301 tmp = ReadByteFromHwPort(baseAddr + 1); 302 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) { 303 tmp = ReadByteFromHwPort(baseAddr + 1); 304 timeout++; 305 } 306 307 return (enc.value ^ 0x800000); 308} 309 310static void C6X_encResetAll(unsigned long baseAddr) 311{ 312 unsigned timeout = 0; 313 314/* printk("Inside C6X_encResetAll\n"); */ 315 316 WriteByteToHwPort(baseAddr, 0x68); 317 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0) 318 && (timeout < C6XDIGIO_TIME_OUT)) { 319 timeout++; 320 } 321 WriteByteToHwPort(baseAddr, 0x6C); 322 timeout = 0; 323 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80) 324 && (timeout < C6XDIGIO_TIME_OUT)) { 325 timeout++; 326 } 327 WriteByteToHwPort(baseAddr, 0x68); 328 timeout = 0; 329 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0) 330 && (timeout < C6XDIGIO_TIME_OUT)) { 331 timeout++; 332 } 333 WriteByteToHwPort(baseAddr, 0x0); 334 timeout = 0; 335 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80) 336 && (timeout < C6XDIGIO_TIME_OUT)) { 337 timeout++; 338 } 339} 340 341static int c6xdigio_pwmo_insn_read(struct comedi_device *dev, 342 struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) 343{ 344 printk("c6xdigio_pwmo_insn_read %x\n", insn->n); 345 return insn->n; 346} 347 348static int c6xdigio_pwmo_insn_write(struct comedi_device *dev, 349 struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) 350{ 351 int i; 352 int chan = CR_CHAN(insn->chanspec); 353 354 /* printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */ 355 for (i = 0; i < insn->n; i++) { 356 C6X_pwmOutput(dev->iobase, chan, data[i]); 357 /* devpriv->ao_readback[chan] = data[i]; */ 358 } 359 return i; 360} 361 362/* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */ 363/* struct comedi_subdevice *s, */ 364/* struct comedi_insn *insn, */ 365/* unsigned int *data) */ 366/* { */ 367/* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */ 368/* return insn->n; */ 369/* } */ 370 371/* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */ 372/* struct comedi_subdevice *s, */ 373/* struct comedi_insn *insn, */ 374/* unsigned int *data) */ 375/* { */ 376/* int i; */ 377/* int chan = CR_CHAN(insn->chanspec); */ 378/* *//* C6X_encResetAll( dev->iobase ); */ 379/* *//* return insn->n; */ 380/* } */ 381 382static int c6xdigio_ei_insn_read(struct comedi_device *dev, 383 struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) 384{ 385 /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */ 386 int n; 387 int chan = CR_CHAN(insn->chanspec); 388 389 for (n = 0; n < insn->n; n++) { 390 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff); 391 } 392 393 return n; 394} 395 396static void board_init(struct comedi_device *dev) 397{ 398 399 /* printk("Inside board_init\n"); */ 400 401 C6X_pwmInit(dev->iobase); 402 C6X_encResetAll(dev->iobase); 403 404} 405 406/* static void board_halt(struct comedi_device *dev) { */ 407/* C6X_pwmInit(dev->iobase); */ 408/* } */ 409 410/* 411 options[0] - I/O port 412 options[1] - irq 413 options[2] - number of encoder chips installed 414 */ 415 416static const struct pnp_device_id c6xdigio_pnp_tbl[] = { 417 /* Standard LPT Printer Port */ 418 {.id = "PNP0400", .driver_data = 0}, 419 /* ECP Printer Port */ 420 {.id = "PNP0401", .driver_data = 0}, 421 {} 422}; 423 424static struct pnp_driver c6xdigio_pnp_driver = { 425 .name = "c6xdigio", 426 .id_table = c6xdigio_pnp_tbl, 427}; 428 429static int c6xdigio_attach(struct comedi_device *dev, struct comedi_devconfig *it) 430{ 431 int result = 0; 432 unsigned long iobase; 433 unsigned int irq; 434 struct comedi_subdevice *s; 435 436 iobase = it->options[0]; 437 printk("comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase); 438 if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) { 439 printk("comedi%d: I/O port conflict\n", dev->minor); 440 return -EIO; 441 } 442 dev->iobase = iobase; 443 dev->board_name = "c6xdigio"; 444 445 result = alloc_subdevices(dev, 2); /* 3 with encoder_init write */ 446 if (result < 0) 447 return result; 448 449 /* Make sure that PnP ports gets activated */ 450 pnp_register_driver(&c6xdigio_pnp_driver); 451 452 irq = it->options[1]; 453 if (irq > 0) { 454 printk("comedi%d: irq = %u ignored\n", dev->minor, irq); 455 } else if (irq == 0) { 456 printk("comedi%d: no irq\n", dev->minor); 457 } 458 459 s = dev->subdevices + 0; 460 /* pwm output subdevice */ 461 s->type = COMEDI_SUBD_AO; /* Not sure what to put here */ 462 s->subdev_flags = SDF_WRITEABLE; 463 s->n_chan = 2; 464 /* s->trig[0] = c6xdigio_pwmo; */ 465 s->insn_read = c6xdigio_pwmo_insn_read; 466 s->insn_write = c6xdigio_pwmo_insn_write; 467 s->maxdata = 500; 468 s->range_table = &range_bipolar10; /* A suitable lie */ 469 470 s = dev->subdevices + 1; 471 /* encoder (counter) subdevice */ 472 s->type = COMEDI_SUBD_COUNTER; 473 s->subdev_flags = SDF_READABLE | SDF_LSAMPL; 474 s->n_chan = 2; 475 /* s->trig[0] = c6xdigio_ei; */ 476 s->insn_read = c6xdigio_ei_insn_read; 477 s->maxdata = 0xffffff; 478 s->range_table = &range_unknown; 479 480 /* s = dev->subdevices + 2; */ 481 /* pwm output subdevice */ 482 /* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */ 483 /* s->subdev_flags = SDF_WRITEABLE; */ 484 /* s->n_chan = 1; */ 485 /* s->trig[0] = c6xdigio_ei_init; */ 486 /* s->insn_read = c6xdigio_ei_init_insn_read; */ 487 /* s->insn_write = c6xdigio_ei_init_insn_write; */ 488 /* s->maxdata = 0xFFFF; // Really just a don't care */ 489 /* s->range_table = &range_unknown; // Not sure what to put here */ 490 491 /* I will call this init anyway but more than likely the DSP board will not be connect */ 492 /* when device driver is loaded. */ 493 board_init(dev); 494 495 return 0; 496} 497 498static int c6xdigio_detach(struct comedi_device *dev) 499{ 500/* board_halt(dev); may not need this */ 501 502 printk("comedi%d: c6xdigio: remove\n", dev->minor); 503 504 if (dev->iobase) { 505 release_region(dev->iobase, C6XDIGIO_SIZE); 506 } 507 if (dev->irq) { 508 free_irq(dev->irq, dev); 509 } /* Not using IRQ so I am not sure if I need this */ 510 pnp_unregister_driver(&c6xdigio_pnp_driver); 511 512 return 0; 513} 514 515COMEDI_INITCLEANUP(driver_c6xdigio); 516