8255.c revision 228ec3402130b158ccd0e690ac5c4a0b27317786
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} 135EXPORT_SYMBOL(subdev_8255_interrupt); 136 137static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) 138{ 139 unsigned long iobase = arg; 140 141 if (dir) { 142 outb(data, iobase + port); 143 return 0; 144 } else { 145 return inb(iobase + port); 146 } 147} 148 149static int subdev_8255_insn(struct comedi_device *dev, 150 struct comedi_subdevice *s, 151 struct comedi_insn *insn, unsigned int *data) 152{ 153 if (data[0]) { 154 s->state &= ~data[0]; 155 s->state |= (data[0] & data[1]); 156 157 if (data[0] & 0xff) 158 CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff, 159 CALLBACK_ARG); 160 if (data[0] & 0xff00) 161 CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff, 162 CALLBACK_ARG); 163 if (data[0] & 0xff0000) 164 CALLBACK_FUNC(1, _8255_DATA + 2, 165 (s->state >> 16) & 0xff, CALLBACK_ARG); 166 } 167 168 data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG); 169 data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8); 170 data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16); 171 172 return 2; 173} 174 175static int subdev_8255_insn_config(struct comedi_device *dev, 176 struct comedi_subdevice *s, 177 struct comedi_insn *insn, unsigned int *data) 178{ 179 unsigned int mask; 180 unsigned int bits; 181 182 mask = 1 << CR_CHAN(insn->chanspec); 183 if (mask & 0x0000ff) 184 bits = 0x0000ff; 185 else if (mask & 0x00ff00) 186 bits = 0x00ff00; 187 else if (mask & 0x0f0000) 188 bits = 0x0f0000; 189 else 190 bits = 0xf00000; 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 s->insn_bits = subdev_8255_insn; 341 s->insn_config = subdev_8255_insn_config; 342 343 s->state = 0; 344 s->io_bits = 0; 345 do_config(dev, s); 346 347 return 0; 348} 349EXPORT_SYMBOL(subdev_8255_init); 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} 369EXPORT_SYMBOL(subdev_8255_init_irq); 370 371void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s) 372{ 373 if (s->private) { 374 /* this test does nothing, so comment it out 375 * if (subdevpriv->have_irq) { 376 * } 377 */ 378 379 kfree(s->private); 380 } 381} 382EXPORT_SYMBOL(subdev_8255_cleanup); 383 384/* 385 386 Start of the 8255 standalone device 387 388 */ 389 390static int dev_8255_attach(struct comedi_device *dev, 391 struct comedi_devconfig *it) 392{ 393 int ret; 394 unsigned long iobase; 395 int i; 396 397 printk("comedi%d: 8255:", dev->minor); 398 399 dev->board_name = "8255"; 400 401 for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) { 402 iobase = it->options[i]; 403 if (!iobase) 404 break; 405 } 406 if (i == 0) { 407 printk(" no devices specified\n"); 408 return -EINVAL; 409 } 410 411 ret = alloc_subdevices(dev, i); 412 if (ret < 0) 413 return ret; 414 415 for (i = 0; i < dev->n_subdevices; i++) { 416 iobase = it->options[i]; 417 418 printk(" 0x%04lx", iobase); 419 if (!request_region(iobase, _8255_SIZE, "8255")) { 420 printk(" (I/O port conflict)"); 421 422 dev->subdevices[i].type = COMEDI_SUBD_UNUSED; 423 } else { 424 subdev_8255_init(dev, dev->subdevices + i, NULL, 425 iobase); 426 } 427 } 428 429 printk("\n"); 430 431 return 0; 432} 433 434static int dev_8255_detach(struct comedi_device *dev) 435{ 436 int i; 437 unsigned long iobase; 438 struct comedi_subdevice *s; 439 440 printk("comedi%d: 8255: remove\n", dev->minor); 441 442 for (i = 0; i < dev->n_subdevices; i++) { 443 s = dev->subdevices + i; 444 if (s->type != COMEDI_SUBD_UNUSED) { 445 iobase = CALLBACK_ARG; 446 release_region(iobase, _8255_SIZE); 447 } 448 subdev_8255_cleanup(dev, s); 449 } 450 451 return 0; 452} 453