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/* 19Driver: 8255 20Description: generic 8255 support 21Devices: [standard] 8255 (8255) 22Author: ds 23Status: works 24Updated: Fri, 7 Jun 2002 12:56:45 -0700 25 26The classic in digital I/O. The 8255 appears in Comedi as a single 27digital I/O subdevice with 24 channels. The channel 0 corresponds 28to the 8255's port A, bit 0; channel 23 corresponds to port C, bit 297. Direction configuration is done in blocks, with channels 0-7, 308-15, 16-19, and 20-23 making up the 4 blocks. The only 8255 mode 31supported is mode 0. 32 33You should enable compilation this driver if you plan to use a board 34that has an 8255 chip. For multifunction boards, the main driver will 35configure the 8255 subdevice automatically. 36 37This driver also works independently with ISA and PCI cards that 38directly map the 8255 registers to I/O ports, including cards with 39multiple 8255 chips. To configure the driver for such a card, the 40option list should be a list of the I/O port bases for each of the 418255 chips. For example, 42 43 comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c 44 45Note that most PCI 8255 boards do NOT work with this driver, and 46need a separate driver as a wrapper. For those that do work, the 47I/O port base address can be found in the output of 'lspci -v'. 48 49*/ 50 51/* 52 This file contains an exported subdevice for driving an 8255. 53 54 To use this subdevice as part of another driver, you need to 55 set up the subdevice in the attach function of the driver by 56 calling: 57 58 subdev_8255_init(device, subdevice, io_function, iobase) 59 60 device and subdevice are pointers to the device and subdevice 61 structures. io_function will be called to provide the 62 low-level input/output to the device, i.e., actual register 63 access. io_function will be called with the value of iobase 64 as the last parameter. If the 8255 device is mapped as 4 65 consecutive I/O ports, you can use NULL for io_function 66 and the I/O port base for iobase, and an internal function will 67 handle the register access. 68 69 In addition, if the main driver handles interrupts, you can 70 enable commands on the subdevice by calling subdev_8255_init_irq() 71 instead. Then, when you get an interrupt that is likely to be 72 from the 8255, you should call subdev_8255_interrupt(), which 73 will copy the latched value to a Comedi buffer. 74 */ 75 76#include <linux/module.h> 77#include "../comedidev.h" 78 79#include "comedi_fc.h" 80#include "8255.h" 81 82struct subdev_8255_private { 83 unsigned long regbase; 84 int (*io)(struct comedi_device *, int, int, int, unsigned long); 85}; 86 87static int subdev_8255_io(struct comedi_device *dev, 88 int dir, int port, int data, unsigned long regbase) 89{ 90 if (dir) { 91 outb(data, dev->iobase + regbase + port); 92 return 0; 93 } 94 return inb(dev->iobase + regbase + port); 95} 96 97static int subdev_8255_mmio(struct comedi_device *dev, 98 int dir, int port, int data, unsigned long regbase) 99{ 100 if (dir) { 101 writeb(data, dev->mmio + regbase + port); 102 return 0; 103 } 104 return readb(dev->mmio + regbase + port); 105} 106 107static int subdev_8255_insn(struct comedi_device *dev, 108 struct comedi_subdevice *s, 109 struct comedi_insn *insn, 110 unsigned int *data) 111{ 112 struct subdev_8255_private *spriv = s->private; 113 unsigned long regbase = spriv->regbase; 114 unsigned int mask; 115 unsigned int v; 116 117 mask = comedi_dio_update_state(s, data); 118 if (mask) { 119 if (mask & 0xff) 120 spriv->io(dev, 1, I8255_DATA_A_REG, 121 s->state & 0xff, regbase); 122 if (mask & 0xff00) 123 spriv->io(dev, 1, I8255_DATA_B_REG, 124 (s->state >> 8) & 0xff, regbase); 125 if (mask & 0xff0000) 126 spriv->io(dev, 1, I8255_DATA_C_REG, 127 (s->state >> 16) & 0xff, regbase); 128 } 129 130 v = spriv->io(dev, 0, I8255_DATA_A_REG, 0, regbase); 131 v |= (spriv->io(dev, 0, I8255_DATA_B_REG, 0, regbase) << 8); 132 v |= (spriv->io(dev, 0, I8255_DATA_C_REG, 0, regbase) << 16); 133 134 data[1] = v; 135 136 return insn->n; 137} 138 139static void subdev_8255_do_config(struct comedi_device *dev, 140 struct comedi_subdevice *s) 141{ 142 struct subdev_8255_private *spriv = s->private; 143 unsigned long regbase = spriv->regbase; 144 int config; 145 146 config = I8255_CTRL_CW; 147 /* 1 in io_bits indicates output, 1 in config indicates input */ 148 if (!(s->io_bits & 0x0000ff)) 149 config |= I8255_CTRL_A_IO; 150 if (!(s->io_bits & 0x00ff00)) 151 config |= I8255_CTRL_B_IO; 152 if (!(s->io_bits & 0x0f0000)) 153 config |= I8255_CTRL_C_LO_IO; 154 if (!(s->io_bits & 0xf00000)) 155 config |= I8255_CTRL_C_HI_IO; 156 157 spriv->io(dev, 1, I8255_CTRL_REG, config, regbase); 158} 159 160static int subdev_8255_insn_config(struct comedi_device *dev, 161 struct comedi_subdevice *s, 162 struct comedi_insn *insn, 163 unsigned int *data) 164{ 165 unsigned int chan = CR_CHAN(insn->chanspec); 166 unsigned int mask; 167 int ret; 168 169 if (chan < 8) 170 mask = 0x0000ff; 171 else if (chan < 16) 172 mask = 0x00ff00; 173 else if (chan < 20) 174 mask = 0x0f0000; 175 else 176 mask = 0xf00000; 177 178 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 179 if (ret) 180 return ret; 181 182 subdev_8255_do_config(dev, s); 183 184 return insn->n; 185} 186 187static int __subdev_8255_init(struct comedi_device *dev, 188 struct comedi_subdevice *s, 189 int (*io)(struct comedi_device *, 190 int, int, int, unsigned long), 191 unsigned long regbase, 192 bool is_mmio) 193{ 194 struct subdev_8255_private *spriv; 195 196 spriv = comedi_alloc_spriv(s, sizeof(*spriv)); 197 if (!spriv) 198 return -ENOMEM; 199 200 if (io) 201 spriv->io = io; 202 else if (is_mmio) 203 spriv->io = subdev_8255_mmio; 204 else 205 spriv->io = subdev_8255_io; 206 spriv->regbase = regbase; 207 208 s->type = COMEDI_SUBD_DIO; 209 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 210 s->n_chan = 24; 211 s->range_table = &range_digital; 212 s->maxdata = 1; 213 s->insn_bits = subdev_8255_insn; 214 s->insn_config = subdev_8255_insn_config; 215 216 subdev_8255_do_config(dev, s); 217 218 return 0; 219} 220 221int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s, 222 int (*io)(struct comedi_device *, 223 int, int, int, unsigned long), 224 unsigned long regbase) 225{ 226 return __subdev_8255_init(dev, s, io, regbase, false); 227} 228EXPORT_SYMBOL_GPL(subdev_8255_init); 229 230int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s, 231 int (*io)(struct comedi_device *, 232 int, int, int, unsigned long), 233 unsigned long regbase) 234{ 235 return __subdev_8255_init(dev, s, io, regbase, true); 236} 237EXPORT_SYMBOL_GPL(subdev_8255_mm_init); 238/* 239 240 Start of the 8255 standalone device 241 242 */ 243 244static int dev_8255_attach(struct comedi_device *dev, 245 struct comedi_devconfig *it) 246{ 247 struct comedi_subdevice *s; 248 unsigned long iobase; 249 int ret; 250 int i; 251 252 for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) { 253 iobase = it->options[i]; 254 if (!iobase) 255 break; 256 } 257 if (i == 0) { 258 dev_warn(dev->class_dev, "no devices specified\n"); 259 return -EINVAL; 260 } 261 262 ret = comedi_alloc_subdevices(dev, i); 263 if (ret) 264 return ret; 265 266 for (i = 0; i < dev->n_subdevices; i++) { 267 s = &dev->subdevices[i]; 268 iobase = it->options[i]; 269 270 /* 271 * __comedi_request_region() does not set dev->iobase. 272 * 273 * For 8255 devices that are manually attached using 274 * comedi_config, the 'iobase' is the actual I/O port 275 * base address of the chip. 276 */ 277 ret = __comedi_request_region(dev, iobase, I8255_SIZE); 278 if (ret) { 279 s->type = COMEDI_SUBD_UNUSED; 280 } else { 281 ret = subdev_8255_init(dev, s, NULL, iobase); 282 if (ret) 283 return ret; 284 } 285 } 286 287 return 0; 288} 289 290static void dev_8255_detach(struct comedi_device *dev) 291{ 292 struct comedi_subdevice *s; 293 struct subdev_8255_private *spriv; 294 int i; 295 296 for (i = 0; i < dev->n_subdevices; i++) { 297 s = &dev->subdevices[i]; 298 if (s->type != COMEDI_SUBD_UNUSED) { 299 spriv = s->private; 300 release_region(spriv->regbase, I8255_SIZE); 301 } 302 } 303} 304 305static struct comedi_driver dev_8255_driver = { 306 .driver_name = "8255", 307 .module = THIS_MODULE, 308 .attach = dev_8255_attach, 309 .detach = dev_8255_detach, 310}; 311module_comedi_driver(dev_8255_driver); 312 313MODULE_AUTHOR("Comedi http://www.comedi.org"); 314MODULE_DESCRIPTION("Comedi low-level driver"); 315MODULE_LICENSE("GPL"); 316