8255.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
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(struct comedi_device *dev, 109 struct comedi_devconfig *it); 110static int dev_8255_detach(struct comedi_device *dev); 111static struct comedi_driver driver_8255 = { 112 .driver_name = "8255", 113 .module = THIS_MODULE, 114 .attach = dev_8255_attach, 115 .detach = dev_8255_detach, 116}; 117 118COMEDI_INITCLEANUP(driver_8255); 119 120static void do_config(struct comedi_device *dev, struct comedi_subdevice *s); 121 122void subdev_8255_interrupt(struct comedi_device *dev, 123 struct comedi_subdevice *s) 124{ 125 short d; 126 127 d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG); 128 d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8); 129 130 comedi_buf_put(s->async, d); 131 s->async->events |= COMEDI_CB_EOS; 132 133 comedi_event(dev, s); 134} 135 136static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) 137{ 138 unsigned long iobase = arg; 139 140 if (dir) { 141 outb(data, iobase + port); 142 return 0; 143 } else { 144 return inb(iobase + port); 145 } 146} 147 148static int subdev_8255_insn(struct comedi_device *dev, 149 struct comedi_subdevice *s, 150 struct comedi_insn *insn, unsigned int *data) 151{ 152 if (data[0]) { 153 s->state &= ~data[0]; 154 s->state |= (data[0] & data[1]); 155 156 if (data[0] & 0xff) 157 CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff, 158 CALLBACK_ARG); 159 if (data[0] & 0xff00) 160 CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff, 161 CALLBACK_ARG); 162 if (data[0] & 0xff0000) 163 CALLBACK_FUNC(1, _8255_DATA + 2, 164 (s->state >> 16) & 0xff, CALLBACK_ARG); 165 } 166 167 data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG); 168 data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8); 169 data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16); 170 171 return 2; 172} 173 174static int subdev_8255_insn_config(struct comedi_device *dev, 175 struct comedi_subdevice *s, 176 struct comedi_insn *insn, unsigned int *data) 177{ 178 unsigned int mask; 179 unsigned int bits; 180 181 mask = 1 << CR_CHAN(insn->chanspec); 182 if (mask & 0x0000ff) { 183 bits = 0x0000ff; 184 } else if (mask & 0x00ff00) { 185 bits = 0x00ff00; 186 } else if (mask & 0x0f0000) { 187 bits = 0x0f0000; 188 } else { 189 bits = 0xf00000; 190 } 191 192 switch (data[0]) { 193 case INSN_CONFIG_DIO_INPUT: 194 s->io_bits &= ~bits; 195 break; 196 case INSN_CONFIG_DIO_OUTPUT: 197 s->io_bits |= bits; 198 break; 199 case INSN_CONFIG_DIO_QUERY: 200 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; 201 return insn->n; 202 break; 203 default: 204 return -EINVAL; 205 } 206 207 do_config(dev, s); 208 209 return 1; 210} 211 212static void do_config(struct comedi_device *dev, struct comedi_subdevice *s) 213{ 214 int config; 215 216 config = CR_CW; 217 /* 1 in io_bits indicates output, 1 in config indicates input */ 218 if (!(s->io_bits & 0x0000ff)) 219 config |= CR_A_IO; 220 if (!(s->io_bits & 0x00ff00)) 221 config |= CR_B_IO; 222 if (!(s->io_bits & 0x0f0000)) 223 config |= CR_C_LO_IO; 224 if (!(s->io_bits & 0xf00000)) 225 config |= CR_C_HI_IO; 226 CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG); 227} 228 229static int subdev_8255_cmdtest(struct comedi_device *dev, 230 struct comedi_subdevice *s, 231 struct comedi_cmd *cmd) 232{ 233 int err = 0; 234 unsigned int tmp; 235 236 /* step 1 */ 237 238 tmp = cmd->start_src; 239 cmd->start_src &= TRIG_NOW; 240 if (!cmd->start_src || tmp != cmd->start_src) 241 err++; 242 243 tmp = cmd->scan_begin_src; 244 cmd->scan_begin_src &= TRIG_EXT; 245 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 246 err++; 247 248 tmp = cmd->convert_src; 249 cmd->convert_src &= TRIG_FOLLOW; 250 if (!cmd->convert_src || tmp != cmd->convert_src) 251 err++; 252 253 tmp = cmd->scan_end_src; 254 cmd->scan_end_src &= TRIG_COUNT; 255 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 256 err++; 257 258 tmp = cmd->stop_src; 259 cmd->stop_src &= TRIG_NONE; 260 if (!cmd->stop_src || tmp != cmd->stop_src) 261 err++; 262 263 if (err) 264 return 1; 265 266 /* step 2 */ 267 268 if (err) 269 return 2; 270 271 /* step 3 */ 272 273 if (cmd->start_arg != 0) { 274 cmd->start_arg = 0; 275 err++; 276 } 277 if (cmd->scan_begin_arg != 0) { 278 cmd->scan_begin_arg = 0; 279 err++; 280 } 281 if (cmd->convert_arg != 0) { 282 cmd->convert_arg = 0; 283 err++; 284 } 285 if (cmd->scan_end_arg != 1) { 286 cmd->scan_end_arg = 1; 287 err++; 288 } 289 if (cmd->stop_arg != 0) { 290 cmd->stop_arg = 0; 291 err++; 292 } 293 294 if (err) 295 return 3; 296 297 /* step 4 */ 298 299 if (err) 300 return 4; 301 302 return 0; 303} 304 305static int subdev_8255_cmd(struct comedi_device *dev, 306 struct comedi_subdevice *s) 307{ 308 /* FIXME */ 309 310 return 0; 311} 312 313static int subdev_8255_cancel(struct comedi_device *dev, 314 struct comedi_subdevice *s) 315{ 316 /* FIXME */ 317 318 return 0; 319} 320 321int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s, 322 int (*cb) (int, int, int, unsigned long), 323 unsigned long arg) 324{ 325 s->type = COMEDI_SUBD_DIO; 326 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 327 s->n_chan = 24; 328 s->range_table = &range_digital; 329 s->maxdata = 1; 330 331 s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL); 332 if (!s->private) 333 return -ENOMEM; 334 335 CALLBACK_ARG = arg; 336 if (cb == NULL) { 337 CALLBACK_FUNC = subdev_8255_cb; 338 } else { 339 CALLBACK_FUNC = cb; 340 } 341 s->insn_bits = subdev_8255_insn; 342 s->insn_config = subdev_8255_insn_config; 343 344 s->state = 0; 345 s->io_bits = 0; 346 do_config(dev, s); 347 348 return 0; 349} 350 351int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s, 352 int (*cb) (int, int, int, unsigned long), 353 unsigned long arg) 354{ 355 int ret; 356 357 ret = subdev_8255_init(dev, s, cb, arg); 358 if (ret < 0) 359 return ret; 360 361 s->do_cmdtest = subdev_8255_cmdtest; 362 s->do_cmd = subdev_8255_cmd; 363 s->cancel = subdev_8255_cancel; 364 365 subdevpriv->have_irq = 1; 366 367 return 0; 368} 369 370void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s) 371{ 372 if (s->private) { 373 /* this test does nothing, so comment it out 374 * if (subdevpriv->have_irq) { 375 * } 376 */ 377 378 kfree(s->private); 379 } 380} 381 382/* 383 384 Start of the 8255 standalone device 385 386 */ 387 388static int dev_8255_attach(struct comedi_device *dev, 389 struct comedi_devconfig *it) 390{ 391 int ret; 392 unsigned long iobase; 393 int i; 394 395 printk("comedi%d: 8255:", dev->minor); 396 397 dev->board_name = "8255"; 398 399 for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) { 400 iobase = it->options[i]; 401 if (!iobase) 402 break; 403 } 404 if (i == 0) { 405 printk(" no devices specified\n"); 406 return -EINVAL; 407 } 408 409 ret = alloc_subdevices(dev, i); 410 if (ret < 0) 411 return ret; 412 413 for (i = 0; i < dev->n_subdevices; i++) { 414 iobase = it->options[i]; 415 416 printk(" 0x%04lx", iobase); 417 if (!request_region(iobase, _8255_SIZE, "8255")) { 418 printk(" (I/O port conflict)"); 419 420 dev->subdevices[i].type = COMEDI_SUBD_UNUSED; 421 } else { 422 subdev_8255_init(dev, dev->subdevices + i, NULL, 423 iobase); 424 } 425 } 426 427 printk("\n"); 428 429 return 0; 430} 431 432static int dev_8255_detach(struct comedi_device *dev) 433{ 434 int i; 435 unsigned long iobase; 436 struct comedi_subdevice *s; 437 438 printk("comedi%d: 8255: remove\n", dev->minor); 439 440 for (i = 0; i < dev->n_subdevices; i++) { 441 s = dev->subdevices + i; 442 if (s->type != COMEDI_SUBD_UNUSED) { 443 iobase = CALLBACK_ARG; 444 release_region(iobase, _8255_SIZE); 445 } 446 subdev_8255_cleanup(dev, s); 447 } 448 449 return 0; 450} 451 452EXPORT_SYMBOL(subdev_8255_init); 453EXPORT_SYMBOL(subdev_8255_init_irq); 454EXPORT_SYMBOL(subdev_8255_cleanup); 455EXPORT_SYMBOL(subdev_8255_interrupt); 456