c6xdigio.c revision 90035c0886b256d75bced13b3b3cea5234aff136
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, 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// 379// C6X_encResetAll( dev->iobase ); 380// 381// return insn->n; 382//} 383 384static int c6xdigio_ei_insn_read(struct comedi_device * dev, 385 struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data) 386{ 387 // printk("c6xdigio_ei__insn_read %x\n", insn->n); 388 int n; 389 int chan = CR_CHAN(insn->chanspec); 390 391 for (n = 0; n < insn->n; n++) { 392 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff); 393 } 394 395 return n; 396} 397 398static void board_init(struct comedi_device * dev) 399{ 400 401 //printk("Inside board_init\n"); 402 403 C6X_pwmInit(dev->iobase); 404 C6X_encResetAll(dev->iobase); 405 406} 407 408//static void board_halt(struct comedi_device *dev) { 409// C6X_pwmInit(dev->iobase); 410//} 411 412/* 413 options[0] - I/O port 414 options[1] - irq 415 options[2] - number of encoder chips installed 416 */ 417 418static const struct pnp_device_id c6xdigio_pnp_tbl[] = { 419 /* Standard LPT Printer Port */ 420 {.id = "PNP0400",.driver_data = 0}, 421 /* ECP Printer Port */ 422 {.id = "PNP0401",.driver_data = 0}, 423 {} 424}; 425 426static struct pnp_driver c6xdigio_pnp_driver = { 427 .name = "c6xdigio", 428 .id_table = c6xdigio_pnp_tbl, 429}; 430 431static int c6xdigio_attach(struct comedi_device * dev, comedi_devconfig * it) 432{ 433 int result = 0; 434 unsigned long iobase; 435 unsigned int irq; 436 struct comedi_subdevice *s; 437 438 iobase = it->options[0]; 439 printk("comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase); 440 if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) { 441 printk("comedi%d: I/O port conflict\n", dev->minor); 442 return -EIO; 443 } 444 dev->iobase = iobase; 445 dev->board_name = "c6xdigio"; 446 447 result = alloc_subdevices(dev, 2); // 3 with encoder_init write 448 if (result < 0) 449 return result; 450 451 // Make sure that PnP ports gets activated 452 pnp_register_driver(&c6xdigio_pnp_driver); 453 454 irq = it->options[1]; 455 if (irq > 0) { 456 printk("comedi%d: irq = %u ignored\n", dev->minor, irq); 457 } else if (irq == 0) { 458 printk("comedi%d: no irq\n", dev->minor); 459 } 460 461 s = dev->subdevices + 0; 462 /* pwm output subdevice */ 463 s->type = COMEDI_SUBD_AO; // Not sure what to put here 464 s->subdev_flags = SDF_WRITEABLE; 465 s->n_chan = 2; 466 /* s->trig[0] = c6xdigio_pwmo; */ 467 s->insn_read = c6xdigio_pwmo_insn_read; 468 s->insn_write = c6xdigio_pwmo_insn_write; 469 s->maxdata = 500; 470 s->range_table = &range_bipolar10; // A suitable lie 471 472 s = dev->subdevices + 1; 473 /* encoder (counter) subdevice */ 474 s->type = COMEDI_SUBD_COUNTER; 475 s->subdev_flags = SDF_READABLE | SDF_LSAMPL; 476 s->n_chan = 2; 477 /* s->trig[0] = c6xdigio_ei; */ 478 s->insn_read = c6xdigio_ei_insn_read; 479 s->maxdata = 0xffffff; 480 s->range_table = &range_unknown; 481 482 // s = dev->subdevices + 2; 483 // /* pwm output subdevice */ 484 // s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here 485 // s->subdev_flags = SDF_WRITEABLE; 486 // s->n_chan = 1; 487 // /* s->trig[0] = c6xdigio_ei_init; */ 488 // s->insn_read = c6xdigio_ei_init_insn_read; 489 // s->insn_write = c6xdigio_ei_init_insn_write; 490 // s->maxdata = 0xFFFF; // Really just a don't care 491 // s->range_table = &range_unknown; // Not sure what to put here 492 493 // I will call this init anyway but more than likely the DSP board will not be connect 494 // when device driver is loaded. 495 board_init(dev); 496 497 return 0; 498} 499 500static int c6xdigio_detach(struct comedi_device * dev) 501{ 502// board_halt(dev); // may not need this 503 504 printk("comedi%d: c6xdigio: remove\n", dev->minor); 505 506 if (dev->iobase) { 507 release_region(dev->iobase, C6XDIGIO_SIZE); 508 } 509 if (dev->irq) { 510 free_irq(dev->irq, dev); 511 } // Not using IRQ so I am not sure if I need this 512 pnp_unregister_driver(&c6xdigio_pnp_driver); 513 514 return 0; 515} 516 517COMEDI_INITCLEANUP(driver_c6xdigio); 518