1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * comedi_8255.c 4 * Generic 8255 digital I/O support 5 * 6 * Split from the Comedi "8255" driver module. 7 * 8 * COMEDI - Linux Control and Measurement Device Interface 9 * Copyright (C) 1998 David A. Schleef <ds@schleef.org> 10 */ 11 12/* 13 * Module: comedi_8255 14 * Description: Generic 8255 support 15 * Author: ds 16 * Updated: Fri, 22 May 2015 12:14:17 +0000 17 * Status: works 18 * 19 * This module is not used directly by end-users. Rather, it is used by 20 * other drivers to provide support for an 8255 "Programmable Peripheral 21 * Interface" (PPI) chip. 22 * 23 * The classic in digital I/O. The 8255 appears in Comedi as a single 24 * digital I/O subdevice with 24 channels. The channel 0 corresponds to 25 * the 8255's port A, bit 0; channel 23 corresponds to port C, bit 7. 26 * Direction configuration is done in blocks, with channels 0-7, 8-15, 27 * 16-19, and 20-23 making up the 4 blocks. The only 8255 mode 28 * supported is mode 0. 29 */ 30 31#include <linux/module.h> 32#include <linux/comedi/comedidev.h> 33#include <linux/comedi/comedi_8255.h> 34 35struct subdev_8255_private { 36 unsigned long context; 37 int (*io)(struct comedi_device *dev, int dir, int port, int data, 38 unsigned long context); 39}; 40 41#ifdef CONFIG_HAS_IOPORT 42 43static int subdev_8255_io(struct comedi_device *dev, 44 int dir, int port, int data, unsigned long regbase) 45{ 46 if (dir) { 47 outb(data, dev->iobase + regbase + port); 48 return 0; 49 } 50 return inb(dev->iobase + regbase + port); 51} 52 53#endif /* CONFIG_HAS_IOPORT */ 54 55static int subdev_8255_mmio(struct comedi_device *dev, 56 int dir, int port, int data, unsigned long regbase) 57{ 58 if (dir) { 59 writeb(data, dev->mmio + regbase + port); 60 return 0; 61 } 62 return readb(dev->mmio + regbase + port); 63} 64 65static int subdev_8255_insn(struct comedi_device *dev, 66 struct comedi_subdevice *s, 67 struct comedi_insn *insn, 68 unsigned int *data) 69{ 70 struct subdev_8255_private *spriv = s->private; 71 unsigned long context = spriv->context; 72 unsigned int mask; 73 unsigned int v; 74 75 mask = comedi_dio_update_state(s, data); 76 if (mask) { 77 if (mask & 0xff) 78 spriv->io(dev, 1, I8255_DATA_A_REG, 79 s->state & 0xff, context); 80 if (mask & 0xff00) 81 spriv->io(dev, 1, I8255_DATA_B_REG, 82 (s->state >> 8) & 0xff, context); 83 if (mask & 0xff0000) 84 spriv->io(dev, 1, I8255_DATA_C_REG, 85 (s->state >> 16) & 0xff, context); 86 } 87 88 v = spriv->io(dev, 0, I8255_DATA_A_REG, 0, context); 89 v |= (spriv->io(dev, 0, I8255_DATA_B_REG, 0, context) << 8); 90 v |= (spriv->io(dev, 0, I8255_DATA_C_REG, 0, context) << 16); 91 92 data[1] = v; 93 94 return insn->n; 95} 96 97static void subdev_8255_do_config(struct comedi_device *dev, 98 struct comedi_subdevice *s) 99{ 100 struct subdev_8255_private *spriv = s->private; 101 unsigned long context = spriv->context; 102 int config; 103 104 config = I8255_CTRL_CW; 105 /* 1 in io_bits indicates output, 1 in config indicates input */ 106 if (!(s->io_bits & 0x0000ff)) 107 config |= I8255_CTRL_A_IO; 108 if (!(s->io_bits & 0x00ff00)) 109 config |= I8255_CTRL_B_IO; 110 if (!(s->io_bits & 0x0f0000)) 111 config |= I8255_CTRL_C_LO_IO; 112 if (!(s->io_bits & 0xf00000)) 113 config |= I8255_CTRL_C_HI_IO; 114 115 spriv->io(dev, 1, I8255_CTRL_REG, config, context); 116} 117 118static int subdev_8255_insn_config(struct comedi_device *dev, 119 struct comedi_subdevice *s, 120 struct comedi_insn *insn, 121 unsigned int *data) 122{ 123 unsigned int chan = CR_CHAN(insn->chanspec); 124 unsigned int mask; 125 int ret; 126 127 if (chan < 8) 128 mask = 0x0000ff; 129 else if (chan < 16) 130 mask = 0x00ff00; 131 else if (chan < 20) 132 mask = 0x0f0000; 133 else 134 mask = 0xf00000; 135 136 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 137 if (ret) 138 return ret; 139 140 subdev_8255_do_config(dev, s); 141 142 return insn->n; 143} 144 145static int __subdev_8255_init(struct comedi_device *dev, 146 struct comedi_subdevice *s, 147 int (*io)(struct comedi_device *dev, 148 int dir, int port, int data, 149 unsigned long context), 150 unsigned long context) 151{ 152 struct subdev_8255_private *spriv; 153 154 if (!io) 155 return -EINVAL; 156 157 spriv = comedi_alloc_spriv(s, sizeof(*spriv)); 158 if (!spriv) 159 return -ENOMEM; 160 161 spriv->context = context; 162 spriv->io = io; 163 164 s->type = COMEDI_SUBD_DIO; 165 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 166 s->n_chan = 24; 167 s->range_table = &range_digital; 168 s->maxdata = 1; 169 s->insn_bits = subdev_8255_insn; 170 s->insn_config = subdev_8255_insn_config; 171 172 subdev_8255_do_config(dev, s); 173 174 return 0; 175} 176 177#ifdef CONFIG_HAS_IOPORT 178 179/** 180 * subdev_8255_io_init - initialize DIO subdevice for driving I/O mapped 8255 181 * @dev: comedi device owning subdevice 182 * @s: comedi subdevice to initialize 183 * @regbase: offset of 8255 registers from dev->iobase 184 * 185 * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip. 186 * 187 * Return: -ENOMEM if failed to allocate memory, zero on success. 188 */ 189int subdev_8255_io_init(struct comedi_device *dev, struct comedi_subdevice *s, 190 unsigned long regbase) 191{ 192 return __subdev_8255_init(dev, s, subdev_8255_io, regbase); 193} 194EXPORT_SYMBOL_GPL(subdev_8255_io_init); 195 196#endif /* CONFIG_HAS_IOPORT */ 197 198/** 199 * subdev_8255_mm_init - initialize DIO subdevice for driving mmio-mapped 8255 200 * @dev: comedi device owning subdevice 201 * @s: comedi subdevice to initialize 202 * @regbase: offset of 8255 registers from dev->mmio 203 * 204 * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip. 205 * 206 * Return: -ENOMEM if failed to allocate memory, zero on success. 207 */ 208int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s, 209 unsigned long regbase) 210{ 211 return __subdev_8255_init(dev, s, subdev_8255_mmio, regbase); 212} 213EXPORT_SYMBOL_GPL(subdev_8255_mm_init); 214 215/** 216 * subdev_8255_cb_init - initialize DIO subdevice for driving callback-mapped 8255 217 * @dev: comedi device owning subdevice 218 * @s: comedi subdevice to initialize 219 * @io: register I/O call-back function 220 * @context: call-back context 221 * 222 * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip. 223 * 224 * The prototype of the I/O call-back function is of the following form: 225 * 226 * int my_8255_callback(struct comedi_device *dev, int dir, int port, 227 * int data, unsigned long context); 228 * 229 * where 'dev', and 'context' match the values passed to this function, 230 * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir' 231 * is the direction (0 for read, 1 for write) and 'data' is the value to be 232 * written. It should return 0 if writing or the value read if reading. 233 * 234 * 235 * Return: -ENOMEM if failed to allocate memory, zero on success. 236 */ 237int subdev_8255_cb_init(struct comedi_device *dev, struct comedi_subdevice *s, 238 int (*io)(struct comedi_device *dev, int dir, int port, 239 int data, unsigned long context), 240 unsigned long context) 241{ 242 return __subdev_8255_init(dev, s, io, context); 243} 244EXPORT_SYMBOL_GPL(subdev_8255_cb_init); 245 246/** 247 * subdev_8255_regbase - get offset of 8255 registers or call-back context 248 * @s: comedi subdevice 249 * 250 * Returns the 'regbase' or 'context' parameter that was previously passed to 251 * subdev_8255_io_init(), subdev_8255_mm_init(), or subdev_8255_cb_init() to 252 * set up the subdevice. Only valid if the subdevice was set up successfully. 253 */ 254unsigned long subdev_8255_regbase(struct comedi_subdevice *s) 255{ 256 struct subdev_8255_private *spriv = s->private; 257 258 return spriv->context; 259} 260EXPORT_SYMBOL_GPL(subdev_8255_regbase); 261 262static int __init comedi_8255_module_init(void) 263{ 264 return 0; 265} 266module_init(comedi_8255_module_init); 267 268static void __exit comedi_8255_module_exit(void) 269{ 270} 271module_exit(comedi_8255_module_exit); 272 273MODULE_AUTHOR("Comedi https://www.comedi.org"); 274MODULE_DESCRIPTION("Comedi: Generic 8255 digital I/O support"); 275MODULE_LICENSE("GPL"); 276