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