8255.c revision 790c55415aa31f4c732729f94d2c3a54f7d3bfc2
1/* 2 comedi/drivers/8255.c 3 Driver for 8255 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 1998 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 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 22*/ 23/* 24Driver: 8255 25Description: generic 8255 support 26Devices: [standard] 8255 (8255) 27Author: ds 28Status: works 29Updated: Fri, 7 Jun 2002 12:56:45 -0700 30 31The classic in digital I/O. The 8255 appears in Comedi as a single 32digital I/O subdevice with 24 channels. The channel 0 corresponds 33to the 8255's port A, bit 0; channel 23 corresponds to port C, bit 347. Direction configuration is done in blocks, with channels 0-7, 358-15, 16-19, and 20-23 making up the 4 blocks. The only 8255 mode 36supported is mode 0. 37 38You should enable compilation this driver if you plan to use a board 39that has an 8255 chip. For multifunction boards, the main driver will 40configure the 8255 subdevice automatically. 41 42This driver also works independently with ISA and PCI cards that 43directly map the 8255 registers to I/O ports, including cards with 44multiple 8255 chips. To configure the driver for such a card, the 45option list should be a list of the I/O port bases for each of the 468255 chips. For example, 47 48 comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c 49 50Note that most PCI 8255 boards do NOT work with this driver, and 51need a separate driver as a wrapper. For those that do work, the 52I/O port base address can be found in the output of 'lspci -v'. 53 54*/ 55 56/* 57 This file contains an exported subdevice for driving an 8255. 58 59 To use this subdevice as part of another driver, you need to 60 set up the subdevice in the attach function of the driver by 61 calling: 62 63 subdev_8255_init(device, subdevice, callback_function, arg) 64 65 device and subdevice are pointers to the device and subdevice 66 structures. callback_function will be called to provide the 67 low-level input/output to the device, i.e., actual register 68 access. callback_function will be called with the value of arg 69 as the last parameter. If the 8255 device is mapped as 4 70 consecutive I/O ports, you can use NULL for callback_function 71 and the I/O port base for arg, and an internal function will 72 handle the register access. 73 74 In addition, if the main driver handles interrupts, you can 75 enable commands on the subdevice by calling subdev_8255_init_irq() 76 instead. Then, when you get an interrupt that is likely to be 77 from the 8255, you should call subdev_8255_interrupt(), which 78 will copy the latched value to a Comedi buffer. 79 */ 80 81#include "../comedidev.h" 82 83#include <linux/ioport.h> 84 85#define _8255_SIZE 4 86 87#define _8255_DATA 0 88#define _8255_CR 3 89 90#define CR_C_LO_IO 0x01 91#define CR_B_IO 0x02 92#define CR_B_MODE 0x04 93#define CR_C_HI_IO 0x08 94#define CR_A_IO 0x10 95#define CR_A_MODE(a) ((a)<<5) 96#define CR_CW 0x80 97 98struct subdev_8255_struct { 99 unsigned long cb_arg; 100 int (*cb_func) (int, int, int, unsigned long); 101 int have_irq; 102}; 103 104#define CALLBACK_ARG (((struct subdev_8255_struct *)s->private)->cb_arg) 105#define CALLBACK_FUNC (((struct subdev_8255_struct *)s->private)->cb_func) 106#define subdevpriv ((struct subdev_8255_struct *)s->private) 107 108static int dev_8255_attach(comedi_device * dev, comedi_devconfig * it); 109static int dev_8255_detach(comedi_device * dev); 110static comedi_driver driver_8255 = { 111 driver_name:"8255", 112 module:THIS_MODULE, 113 attach:dev_8255_attach, 114 detach:dev_8255_detach, 115}; 116 117COMEDI_INITCLEANUP(driver_8255); 118 119static void do_config(comedi_device * dev, comedi_subdevice * s); 120 121void subdev_8255_interrupt(comedi_device * dev, comedi_subdevice * s) 122{ 123 short d; 124 125 d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG); 126 d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8); 127 128 comedi_buf_put(s->async, d); 129 s->async->events |= COMEDI_CB_EOS; 130 131 comedi_event(dev, s); 132} 133 134static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) 135{ 136 unsigned long iobase = arg; 137 138 if (dir) { 139 outb(data, iobase + port); 140 return 0; 141 } else { 142 return inb(iobase + port); 143 } 144} 145 146static int subdev_8255_insn(comedi_device * dev, comedi_subdevice * s, 147 comedi_insn * insn, unsigned int * data) 148{ 149 if (data[0]) { 150 s->state &= ~data[0]; 151 s->state |= (data[0] & data[1]); 152 153 if (data[0] & 0xff) 154 CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff, 155 CALLBACK_ARG); 156 if (data[0] & 0xff00) 157 CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff, 158 CALLBACK_ARG); 159 if (data[0] & 0xff0000) 160 CALLBACK_FUNC(1, _8255_DATA + 2, 161 (s->state >> 16) & 0xff, CALLBACK_ARG); 162 } 163 164 data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG); 165 data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8); 166 data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16); 167 168 return 2; 169} 170 171static int subdev_8255_insn_config(comedi_device * dev, comedi_subdevice * s, 172 comedi_insn * insn, unsigned int * data) 173{ 174 unsigned int mask; 175 unsigned int bits; 176 177 mask = 1 << CR_CHAN(insn->chanspec); 178 if (mask & 0x0000ff) { 179 bits = 0x0000ff; 180 } else if (mask & 0x00ff00) { 181 bits = 0x00ff00; 182 } else if (mask & 0x0f0000) { 183 bits = 0x0f0000; 184 } else { 185 bits = 0xf00000; 186 } 187 188 switch (data[0]) { 189 case INSN_CONFIG_DIO_INPUT: 190 s->io_bits &= ~bits; 191 break; 192 case INSN_CONFIG_DIO_OUTPUT: 193 s->io_bits |= bits; 194 break; 195 case INSN_CONFIG_DIO_QUERY: 196 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; 197 return insn->n; 198 break; 199 default: 200 return -EINVAL; 201 } 202 203 do_config(dev, s); 204 205 return 1; 206} 207 208static void do_config(comedi_device * dev, comedi_subdevice * s) 209{ 210 int config; 211 212 config = CR_CW; 213 /* 1 in io_bits indicates output, 1 in config indicates input */ 214 if (!(s->io_bits & 0x0000ff)) 215 config |= CR_A_IO; 216 if (!(s->io_bits & 0x00ff00)) 217 config |= CR_B_IO; 218 if (!(s->io_bits & 0x0f0000)) 219 config |= CR_C_LO_IO; 220 if (!(s->io_bits & 0xf00000)) 221 config |= CR_C_HI_IO; 222 CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG); 223} 224 225static int subdev_8255_cmdtest(comedi_device * dev, comedi_subdevice * s, 226 comedi_cmd * cmd) 227{ 228 int err = 0; 229 unsigned int tmp; 230 231 /* step 1 */ 232 233 tmp = cmd->start_src; 234 cmd->start_src &= TRIG_NOW; 235 if (!cmd->start_src || tmp != cmd->start_src) 236 err++; 237 238 tmp = cmd->scan_begin_src; 239 cmd->scan_begin_src &= TRIG_EXT; 240 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 241 err++; 242 243 tmp = cmd->convert_src; 244 cmd->convert_src &= TRIG_FOLLOW; 245 if (!cmd->convert_src || tmp != cmd->convert_src) 246 err++; 247 248 tmp = cmd->scan_end_src; 249 cmd->scan_end_src &= TRIG_COUNT; 250 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 251 err++; 252 253 tmp = cmd->stop_src; 254 cmd->stop_src &= TRIG_NONE; 255 if (!cmd->stop_src || tmp != cmd->stop_src) 256 err++; 257 258 if (err) 259 return 1; 260 261 /* step 2 */ 262 263 if (err) 264 return 2; 265 266 /* step 3 */ 267 268 if (cmd->start_arg != 0) { 269 cmd->start_arg = 0; 270 err++; 271 } 272 if (cmd->scan_begin_arg != 0) { 273 cmd->scan_begin_arg = 0; 274 err++; 275 } 276 if (cmd->convert_arg != 0) { 277 cmd->convert_arg = 0; 278 err++; 279 } 280 if (cmd->scan_end_arg != 1) { 281 cmd->scan_end_arg = 1; 282 err++; 283 } 284 if (cmd->stop_arg != 0) { 285 cmd->stop_arg = 0; 286 err++; 287 } 288 289 if (err) 290 return 3; 291 292 /* step 4 */ 293 294 if (err) 295 return 4; 296 297 return 0; 298} 299 300static int subdev_8255_cmd(comedi_device * dev, comedi_subdevice * s) 301{ 302 /* FIXME */ 303 304 return 0; 305} 306 307static int subdev_8255_cancel(comedi_device * dev, comedi_subdevice * s) 308{ 309 /* FIXME */ 310 311 return 0; 312} 313 314int subdev_8255_init(comedi_device * dev, comedi_subdevice * s, int (*cb) (int, 315 int, int, unsigned long), unsigned long arg) 316{ 317 s->type = COMEDI_SUBD_DIO; 318 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 319 s->n_chan = 24; 320 s->range_table = &range_digital; 321 s->maxdata = 1; 322 323 s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL); 324 if (!s->private) 325 return -ENOMEM; 326 327 CALLBACK_ARG = arg; 328 if (cb == NULL) { 329 CALLBACK_FUNC = subdev_8255_cb; 330 } else { 331 CALLBACK_FUNC = cb; 332 } 333 s->insn_bits = subdev_8255_insn; 334 s->insn_config = subdev_8255_insn_config; 335 336 s->state = 0; 337 s->io_bits = 0; 338 do_config(dev, s); 339 340 return 0; 341} 342 343int subdev_8255_init_irq(comedi_device * dev, comedi_subdevice * s, 344 int (*cb) (int, int, int, unsigned long), unsigned long arg) 345{ 346 int ret; 347 348 ret = subdev_8255_init(dev, s, cb, arg); 349 if (ret < 0) 350 return ret; 351 352 s->do_cmdtest = subdev_8255_cmdtest; 353 s->do_cmd = subdev_8255_cmd; 354 s->cancel = subdev_8255_cancel; 355 356 subdevpriv->have_irq = 1; 357 358 return 0; 359} 360 361void subdev_8255_cleanup(comedi_device * dev, comedi_subdevice * s) 362{ 363 if (s->private) { 364 if (subdevpriv->have_irq) { 365 } 366 367 kfree(s->private); 368 } 369} 370 371/* 372 373 Start of the 8255 standalone device 374 375 */ 376 377static int dev_8255_attach(comedi_device * dev, comedi_devconfig * it) 378{ 379 int ret; 380 unsigned long iobase; 381 int i; 382 383 printk("comedi%d: 8255:", dev->minor); 384 385 dev->board_name = "8255"; 386 387 for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) { 388 iobase = it->options[i]; 389 if (!iobase) 390 break; 391 } 392 if (i == 0) { 393 printk(" no devices specified\n"); 394 return -EINVAL; 395 } 396 397 if ((ret = alloc_subdevices(dev, i)) < 0) 398 return ret; 399 400 for (i = 0; i < dev->n_subdevices; i++) { 401 iobase = it->options[i]; 402 403 printk(" 0x%04lx", iobase); 404 if (!request_region(iobase, _8255_SIZE, "8255")) { 405 printk(" (I/O port conflict)"); 406 407 dev->subdevices[i].type = COMEDI_SUBD_UNUSED; 408 } else { 409 subdev_8255_init(dev, dev->subdevices + i, NULL, 410 iobase); 411 } 412 } 413 414 printk("\n"); 415 416 return 0; 417} 418 419static int dev_8255_detach(comedi_device * dev) 420{ 421 int i; 422 unsigned long iobase; 423 comedi_subdevice *s; 424 425 printk("comedi%d: 8255: remove\n", dev->minor); 426 427 for (i = 0; i < dev->n_subdevices; i++) { 428 s = dev->subdevices + i; 429 if (s->type != COMEDI_SUBD_UNUSED) { 430 iobase = CALLBACK_ARG; 431 release_region(iobase, _8255_SIZE); 432 } 433 subdev_8255_cleanup(dev, s); 434 } 435 436 return 0; 437} 438 439EXPORT_SYMBOL(subdev_8255_init); 440EXPORT_SYMBOL(subdev_8255_init_irq); 441EXPORT_SYMBOL(subdev_8255_cleanup); 442EXPORT_SYMBOL(subdev_8255_interrupt); 443