adl_pci8164.c revision 8c0690eaf7a666183fb9c49d275d9f986acd4239
1/* 2 comedi/drivers/adl_pci8164.c 3 4 Hardware comedi driver fot PCI-8164 Adlink card 5 Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca> 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 21*/ 22/* 23Driver: adl_pci8164 24Description: Driver for the Adlink PCI-8164 4 Axes Motion Control board 25Devices: [ADLink] PCI-8164 (adl_pci8164) 26Author: Michel Lachaine <mike@mikelachaine.ca> 27Status: experimental 28Updated: Mon, 14 Apr 2008 15:10:32 +0100 29 30Configuration Options: 31 [0] - PCI bus of device (optional) 32 [1] - PCI slot of device (optional) 33 If bus/slot is not specified, the first supported 34 PCI device found will be used. 35*/ 36 37#include "../comedidev.h" 38#include <linux/kernel.h> 39#include <linux/delay.h> 40#include "comedi_fc.h" 41#include "comedi_pci.h" 42#include "8253.h" 43 44#define PCI8164_AXIS_X 0x00 45#define PCI8164_AXIS_Y 0x08 46#define PCI8164_AXIS_Z 0x10 47#define PCI8164_AXIS_U 0x18 48 49#define PCI8164_MSTS 0x00 50#define PCI8164_SSTS 0x02 51#define PCI8164_BUF0 0x04 52#define PCI8164_BUF1 0x06 53 54#define PCI8164_CMD 0x00 55#define PCI8164_OTP 0x02 56 57#define PCI_DEVICE_ID_PCI8164 0x8164 58 59static DEFINE_PCI_DEVICE_TABLE(adl_pci8164_pci_table) = { 60 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI8164) }, 61 {0} 62}; 63 64MODULE_DEVICE_TABLE(pci, adl_pci8164_pci_table); 65 66struct adl_pci8164_private { 67 int data; 68 struct pci_dev *pci_dev; 69}; 70 71#define devpriv ((struct adl_pci8164_private *)dev->private) 72 73static int adl_pci8164_attach(struct comedi_device *dev, 74 struct comedi_devconfig *it); 75static int adl_pci8164_detach(struct comedi_device *dev); 76static struct comedi_driver driver_adl_pci8164 = { 77 .driver_name = "adl_pci8164", 78 .module = THIS_MODULE, 79 .attach = adl_pci8164_attach, 80 .detach = adl_pci8164_detach, 81}; 82 83static int adl_pci8164_insn_read_msts(struct comedi_device *dev, 84 struct comedi_subdevice *s, 85 struct comedi_insn *insn, 86 unsigned int *data); 87 88static int adl_pci8164_insn_read_ssts(struct comedi_device *dev, 89 struct comedi_subdevice *s, 90 struct comedi_insn *insn, 91 unsigned int *data); 92 93static int adl_pci8164_insn_read_buf0(struct comedi_device *dev, 94 struct comedi_subdevice *s, 95 struct comedi_insn *insn, 96 unsigned int *data); 97 98static int adl_pci8164_insn_read_buf1(struct comedi_device *dev, 99 struct comedi_subdevice *s, 100 struct comedi_insn *insn, 101 unsigned int *data); 102 103static int adl_pci8164_insn_write_cmd(struct comedi_device *dev, 104 struct comedi_subdevice *s, 105 struct comedi_insn *insn, 106 unsigned int *data); 107 108static int adl_pci8164_insn_write_otp(struct comedi_device *dev, 109 struct comedi_subdevice *s, 110 struct comedi_insn *insn, 111 unsigned int *data); 112 113static int adl_pci8164_insn_write_buf0(struct comedi_device *dev, 114 struct comedi_subdevice *s, 115 struct comedi_insn *insn, 116 unsigned int *data); 117 118static int adl_pci8164_insn_write_buf1(struct comedi_device *dev, 119 struct comedi_subdevice *s, 120 struct comedi_insn *insn, 121 unsigned int *data); 122 123static int adl_pci8164_attach(struct comedi_device *dev, 124 struct comedi_devconfig *it) 125{ 126 struct pci_dev *pcidev = NULL; 127 struct comedi_subdevice *s; 128 int bus, slot; 129 130 printk(KERN_INFO "comedi: attempt to attach...\n"); 131 printk(KERN_INFO "comedi%d: adl_pci8164\n", dev->minor); 132 133 dev->board_name = "pci8164"; 134 bus = it->options[0]; 135 slot = it->options[1]; 136 137 if (alloc_private(dev, sizeof(struct adl_pci8164_private)) < 0) 138 return -ENOMEM; 139 140 if (alloc_subdevices(dev, 4) < 0) 141 return -ENOMEM; 142 143 for_each_pci_dev(pcidev) { 144 if (pcidev->vendor == PCI_VENDOR_ID_ADLINK && 145 pcidev->device == PCI_DEVICE_ID_PCI8164) { 146 if (bus || slot) { 147 /* requested particular bus/slot */ 148 if (pcidev->bus->number != bus 149 || PCI_SLOT(pcidev->devfn) != slot) 150 continue; 151 } 152 devpriv->pci_dev = pcidev; 153 if (comedi_pci_enable(pcidev, "adl_pci8164") < 0) { 154 printk(KERN_ERR "comedi%d: Failed to enable " 155 "PCI device and request regions\n", dev->minor); 156 return -EIO; 157 } 158 dev->iobase = pci_resource_start(pcidev, 2); 159 printk(KERN_DEBUG "comedi: base addr %4lx\n", 160 dev->iobase); 161 162 s = dev->subdevices + 0; 163 s->type = COMEDI_SUBD_PROC; 164 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 165 s->n_chan = 4; 166 s->maxdata = 0xffff; 167 s->len_chanlist = 4; 168 /* s->range_table = &range_axis; */ 169 s->insn_read = adl_pci8164_insn_read_msts; 170 s->insn_write = adl_pci8164_insn_write_cmd; 171 172 s = dev->subdevices + 1; 173 s->type = COMEDI_SUBD_PROC; 174 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 175 s->n_chan = 4; 176 s->maxdata = 0xffff; 177 s->len_chanlist = 4; 178 /* s->range_table = &range_axis; */ 179 s->insn_read = adl_pci8164_insn_read_ssts; 180 s->insn_write = adl_pci8164_insn_write_otp; 181 182 s = dev->subdevices + 2; 183 s->type = COMEDI_SUBD_PROC; 184 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 185 s->n_chan = 4; 186 s->maxdata = 0xffff; 187 s->len_chanlist = 4; 188 /* s->range_table = &range_axis; */ 189 s->insn_read = adl_pci8164_insn_read_buf0; 190 s->insn_write = adl_pci8164_insn_write_buf0; 191 192 s = dev->subdevices + 3; 193 s->type = COMEDI_SUBD_PROC; 194 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 195 s->n_chan = 4; 196 s->maxdata = 0xffff; 197 s->len_chanlist = 4; 198 /* s->range_table = &range_axis; */ 199 s->insn_read = adl_pci8164_insn_read_buf1; 200 s->insn_write = adl_pci8164_insn_write_buf1; 201 202 printk(KERN_INFO "comedi: attached\n"); 203 204 return 1; 205 } 206 } 207 208 printk(KERN_ERR "comedi%d: no supported board found!" 209 "(req. bus/slot : %d/%d)\n", dev->minor, bus, slot); 210 return -EIO; 211} 212 213static int adl_pci8164_detach(struct comedi_device *dev) 214{ 215 printk(KERN_INFO "comedi%d: pci8164: remove\n", dev->minor); 216 217 if (devpriv && devpriv->pci_dev) { 218 if (dev->iobase) 219 comedi_pci_disable(devpriv->pci_dev); 220 pci_dev_put(devpriv->pci_dev); 221 } 222 223 return 0; 224} 225 226/* 227 all the read commands are the same except for the addition a constant 228 * const to the data for inw() 229 */ 230static void adl_pci8164_insn_read(struct comedi_device *dev, 231 struct comedi_subdevice *s, 232 struct comedi_insn *insn, 233 unsigned int *data, 234 char *action, unsigned short offset) 235{ 236 int axis, axis_reg; 237 char *axisname; 238 239 axis = CR_CHAN(insn->chanspec); 240 241 switch (axis) { 242 case 0: 243 axis_reg = PCI8164_AXIS_X; 244 axisname = "X"; 245 break; 246 case 1: 247 axis_reg = PCI8164_AXIS_Y; 248 axisname = "Y"; 249 break; 250 case 2: 251 axis_reg = PCI8164_AXIS_Z; 252 axisname = "Z"; 253 break; 254 case 3: 255 axis_reg = PCI8164_AXIS_U; 256 axisname = "U"; 257 break; 258 default: 259 axis_reg = PCI8164_AXIS_X; 260 axisname = "X"; 261 } 262 263 data[0] = inw(dev->iobase + axis_reg + offset); 264 printk(KERN_DEBUG "comedi: pci8164 %s read -> " 265 "%04X:%04X on axis %s\n", 266 action, data[0], data[1], axisname); 267} 268 269static int adl_pci8164_insn_read_msts(struct comedi_device *dev, 270 struct comedi_subdevice *s, 271 struct comedi_insn *insn, 272 unsigned int *data) 273{ 274 adl_pci8164_insn_read(dev, s, insn, data, "MSTS", PCI8164_MSTS); 275 return 2; 276} 277 278static int adl_pci8164_insn_read_ssts(struct comedi_device *dev, 279 struct comedi_subdevice *s, 280 struct comedi_insn *insn, 281 unsigned int *data) 282{ 283 adl_pci8164_insn_read(dev, s, insn, data, "SSTS", PCI8164_SSTS); 284 return 2; 285} 286 287static int adl_pci8164_insn_read_buf0(struct comedi_device *dev, 288 struct comedi_subdevice *s, 289 struct comedi_insn *insn, 290 unsigned int *data) 291{ 292 adl_pci8164_insn_read(dev, s, insn, data, "BUF0", PCI8164_BUF0); 293 return 2; 294} 295 296static int adl_pci8164_insn_read_buf1(struct comedi_device *dev, 297 struct comedi_subdevice *s, 298 struct comedi_insn *insn, 299 unsigned int *data) 300{ 301 adl_pci8164_insn_read(dev, s, insn, data, "BUF1", PCI8164_BUF1); 302 return 2; 303} 304 305/* 306 all the write commands are the same except for the addition a constant 307 * const to the data for outw() 308 */ 309static void adl_pci8164_insn_out(struct comedi_device *dev, 310 struct comedi_subdevice *s, 311 struct comedi_insn *insn, 312 unsigned int *data, 313 char *action, unsigned short offset) 314{ 315 unsigned int axis, axis_reg; 316 317 char *axisname; 318 319 axis = CR_CHAN(insn->chanspec); 320 321 switch (axis) { 322 case 0: 323 axis_reg = PCI8164_AXIS_X; 324 axisname = "X"; 325 break; 326 case 1: 327 axis_reg = PCI8164_AXIS_Y; 328 axisname = "Y"; 329 break; 330 case 2: 331 axis_reg = PCI8164_AXIS_Z; 332 axisname = "Z"; 333 break; 334 case 3: 335 axis_reg = PCI8164_AXIS_U; 336 axisname = "U"; 337 break; 338 default: 339 axis_reg = PCI8164_AXIS_X; 340 axisname = "X"; 341 } 342 343 outw(data[0], dev->iobase + axis_reg + offset); 344 345 printk(KERN_DEBUG "comedi: pci8164 %s write -> " 346 "%04X:%04X on axis %s\n", 347 action, data[0], data[1], axisname); 348 349} 350 351static int adl_pci8164_insn_write_cmd(struct comedi_device *dev, 352 struct comedi_subdevice *s, 353 struct comedi_insn *insn, 354 unsigned int *data) 355{ 356 adl_pci8164_insn_out(dev, s, insn, data, "CMD", PCI8164_CMD); 357 return 2; 358} 359 360static int adl_pci8164_insn_write_otp(struct comedi_device *dev, 361 struct comedi_subdevice *s, 362 struct comedi_insn *insn, 363 unsigned int *data) 364{ 365 adl_pci8164_insn_out(dev, s, insn, data, "OTP", PCI8164_OTP); 366 return 2; 367} 368 369static int adl_pci8164_insn_write_buf0(struct comedi_device *dev, 370 struct comedi_subdevice *s, 371 struct comedi_insn *insn, 372 unsigned int *data) 373{ 374 adl_pci8164_insn_out(dev, s, insn, data, "BUF0", PCI8164_BUF0); 375 return 2; 376} 377 378static int adl_pci8164_insn_write_buf1(struct comedi_device *dev, 379 struct comedi_subdevice *s, 380 struct comedi_insn *insn, 381 unsigned int *data) 382{ 383 adl_pci8164_insn_out(dev, s, insn, data, "BUF1", PCI8164_BUF1); 384 return 2; 385} 386 387static int __devinit driver_adl_pci8164_pci_probe(struct pci_dev *dev, 388 const struct pci_device_id 389 *ent) 390{ 391 return comedi_pci_auto_config(dev, driver_adl_pci8164.driver_name); 392} 393 394static void __devexit driver_adl_pci8164_pci_remove(struct pci_dev *dev) 395{ 396 comedi_pci_auto_unconfig(dev); 397} 398 399static struct pci_driver driver_adl_pci8164_pci_driver = { 400 .id_table = adl_pci8164_pci_table, 401 .probe = &driver_adl_pci8164_pci_probe, 402 .remove = __devexit_p(&driver_adl_pci8164_pci_remove) 403}; 404 405static int __init driver_adl_pci8164_init_module(void) 406{ 407 int retval; 408 409 retval = comedi_driver_register(&driver_adl_pci8164); 410 if (retval < 0) 411 return retval; 412 413 driver_adl_pci8164_pci_driver.name = 414 (char *)driver_adl_pci8164.driver_name; 415 return pci_register_driver(&driver_adl_pci8164_pci_driver); 416} 417 418static void __exit driver_adl_pci8164_cleanup_module(void) 419{ 420 pci_unregister_driver(&driver_adl_pci8164_pci_driver); 421 comedi_driver_unregister(&driver_adl_pci8164); 422} 423 424module_init(driver_adl_pci8164_init_module); 425module_exit(driver_adl_pci8164_cleanup_module); 426 427MODULE_AUTHOR("Comedi http://www.comedi.org"); 428MODULE_DESCRIPTION("Comedi low-level driver"); 429MODULE_LICENSE("GPL"); 430