8255.c revision c4d30ee861c3a3a361f934e6f96b1d07a2889976
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, struct comedi_devconfig * it); 109static int dev_8255_detach(struct comedi_device *dev); 110static struct 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(struct comedi_device *dev, struct comedi_subdevice * s); 120 121void subdev_8255_interrupt(struct comedi_device *dev, struct 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(struct comedi_device *dev, struct comedi_subdevice * s, 147 struct 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(struct comedi_device *dev, struct comedi_subdevice * s, 172 struct 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(struct comedi_device *dev, struct 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(struct comedi_device *dev, struct comedi_subdevice * s, 226 struct 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(struct comedi_device *dev, struct comedi_subdevice * s) 301{ 302 /* FIXME */ 303 304 return 0; 305} 306 307static int subdev_8255_cancel(struct comedi_device *dev, struct comedi_subdevice * s) 308{ 309 /* FIXME */ 310 311 return 0; 312} 313 314int subdev_8255_init(struct comedi_device *dev, struct 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(struct comedi_device *dev, struct 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(struct comedi_device *dev, struct comedi_subdevice * s) 362{ 363 if (s->private) { 364 /* this test does nothing, so comment it out 365 * if (subdevpriv->have_irq) { 366 * } 367 */ 368 369 kfree(s->private); 370 } 371} 372 373/* 374 375 Start of the 8255 standalone device 376 377 */ 378 379static int dev_8255_attach(struct comedi_device *dev, struct comedi_devconfig * it) 380{ 381 int ret; 382 unsigned long iobase; 383 int i; 384 385 printk("comedi%d: 8255:", dev->minor); 386 387 dev->board_name = "8255"; 388 389 for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) { 390 iobase = it->options[i]; 391 if (!iobase) 392 break; 393 } 394 if (i == 0) { 395 printk(" no devices specified\n"); 396 return -EINVAL; 397 } 398 399 ret = alloc_subdevices(dev, i); 400 if (ret < 0) 401 return ret; 402 403 for (i = 0; i < dev->n_subdevices; i++) { 404 iobase = it->options[i]; 405 406 printk(" 0x%04lx", iobase); 407 if (!request_region(iobase, _8255_SIZE, "8255")) { 408 printk(" (I/O port conflict)"); 409 410 dev->subdevices[i].type = COMEDI_SUBD_UNUSED; 411 } else { 412 subdev_8255_init(dev, dev->subdevices + i, NULL, 413 iobase); 414 } 415 } 416 417 printk("\n"); 418 419 return 0; 420} 421 422static int dev_8255_detach(struct comedi_device *dev) 423{ 424 int i; 425 unsigned long iobase; 426 struct comedi_subdevice *s; 427 428 printk("comedi%d: 8255: remove\n", dev->minor); 429 430 for (i = 0; i < dev->n_subdevices; i++) { 431 s = dev->subdevices + i; 432 if (s->type != COMEDI_SUBD_UNUSED) { 433 iobase = CALLBACK_ARG; 434 release_region(iobase, _8255_SIZE); 435 } 436 subdev_8255_cleanup(dev, s); 437 } 438 439 return 0; 440} 441 442EXPORT_SYMBOL(subdev_8255_init); 443EXPORT_SYMBOL(subdev_8255_init_irq); 444EXPORT_SYMBOL(subdev_8255_cleanup); 445EXPORT_SYMBOL(subdev_8255_interrupt); 446