From 968d17786d74d2a637ab742a752e9302316f2194 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 22 May 2015 16:21:38 +0100 Subject: [PATCH] staging: comedi: comedi_8255: new module split from 8255 The Comedi "8255" module is both a standalone Comedi device driver module for simple devices with one or more 8255 "Programmable Peripheral Interface" chips at known I/O base addresses (configured at run-time), and a helper module to configure a 8255-based digital I/O subdevice for other Comedi drivers. Split the "8255 subdevice helper" functionality into a new module: "comedi_8255", leaving the standalone 8255 Comedi driver in the "8255" module. The Comedi "detach" routine of the standalone "8255" driver needs to retrieve the I/O base address passed to the "comedi_8255" module to set up each subdevice in order to release the I/O port regions it requested in its "attach" routine. The "comedi_8255" module stores it in a "subdevice private" data structure that is no longer known to the "8255" module, so add a new, exported function `subdev_8255_regbase()` to retrieve it. Signed-off-by: Ian Abbott Reviewed-by: H Hartley Sweeten Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/Kconfig | 18 +- drivers/staging/comedi/drivers/8255.c | 223 +-------------- drivers/staging/comedi/drivers/8255.h | 2 + drivers/staging/comedi/drivers/Makefile | 3 +- drivers/staging/comedi/drivers/comedi_8255.c | 285 +++++++++++++++++++ 5 files changed, 305 insertions(+), 226 deletions(-) create mode 100644 drivers/staging/comedi/drivers/comedi_8255.c diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig index 61c6351f55ac..7dee73dfbf88 100644 --- a/drivers/staging/comedi/Kconfig +++ b/drivers/staging/comedi/Kconfig @@ -1247,16 +1247,22 @@ config COMEDI_8254 tristate config COMEDI_8255 - tristate "Generic 8255 support" + tristate + +config COMEDI_8255_SA + tristate "Standalone 8255 support" + select COMEDI_8255 ---help--- - Enable generic 8255 support. + Enable support for 8255 digital I/O as a standalone driver. You should enable compilation this driver if you plan to use a board - that has an 8255 chip. For multifunction boards, the main driver will - configure the 8255 subdevice automatically. + that has an 8255 chip at a known I/O base address and there are no + other Comedi drivers for the board. - Note that most PCI based 8255 boards use the 8255_pci driver as a - wrapper around this driver. + Note that Comedi drivers for most multi-function boards incorporating + an 8255 chip use the 'comedi_8255' module. Most PCI-based 8255 + boards use the 8255_pci driver as a wrapper around the 'comedi_8255' + module. To compile this driver as a module, choose M here: the module will be called 8255. diff --git a/drivers/staging/comedi/drivers/8255.c b/drivers/staging/comedi/drivers/8255.c index f87a27e540b1..b79d3764a8a0 100644 --- a/drivers/staging/comedi/drivers/8255.c +++ b/drivers/staging/comedi/drivers/8255.c @@ -53,221 +53,6 @@ #include "8255.h" -struct subdev_8255_private { - unsigned long regbase; - int (*io)(struct comedi_device *dev, int dir, int port, int data, - unsigned long regbase); -}; - -static int subdev_8255_io(struct comedi_device *dev, - int dir, int port, int data, unsigned long regbase) -{ - if (dir) { - outb(data, dev->iobase + regbase + port); - return 0; - } - return inb(dev->iobase + regbase + port); -} - -static int subdev_8255_mmio(struct comedi_device *dev, - int dir, int port, int data, unsigned long regbase) -{ - if (dir) { - writeb(data, dev->mmio + regbase + port); - return 0; - } - return readb(dev->mmio + regbase + port); -} - -static int subdev_8255_insn(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct subdev_8255_private *spriv = s->private; - unsigned long regbase = spriv->regbase; - unsigned int mask; - unsigned int v; - - mask = comedi_dio_update_state(s, data); - if (mask) { - if (mask & 0xff) - spriv->io(dev, 1, I8255_DATA_A_REG, - s->state & 0xff, regbase); - if (mask & 0xff00) - spriv->io(dev, 1, I8255_DATA_B_REG, - (s->state >> 8) & 0xff, regbase); - if (mask & 0xff0000) - spriv->io(dev, 1, I8255_DATA_C_REG, - (s->state >> 16) & 0xff, regbase); - } - - v = spriv->io(dev, 0, I8255_DATA_A_REG, 0, regbase); - v |= (spriv->io(dev, 0, I8255_DATA_B_REG, 0, regbase) << 8); - v |= (spriv->io(dev, 0, I8255_DATA_C_REG, 0, regbase) << 16); - - data[1] = v; - - return insn->n; -} - -static void subdev_8255_do_config(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - struct subdev_8255_private *spriv = s->private; - unsigned long regbase = spriv->regbase; - int config; - - config = I8255_CTRL_CW; - /* 1 in io_bits indicates output, 1 in config indicates input */ - if (!(s->io_bits & 0x0000ff)) - config |= I8255_CTRL_A_IO; - if (!(s->io_bits & 0x00ff00)) - config |= I8255_CTRL_B_IO; - if (!(s->io_bits & 0x0f0000)) - config |= I8255_CTRL_C_LO_IO; - if (!(s->io_bits & 0xf00000)) - config |= I8255_CTRL_C_HI_IO; - - spriv->io(dev, 1, I8255_CTRL_REG, config, regbase); -} - -static int subdev_8255_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int mask; - int ret; - - if (chan < 8) - mask = 0x0000ff; - else if (chan < 16) - mask = 0x00ff00; - else if (chan < 20) - mask = 0x0f0000; - else - mask = 0xf00000; - - ret = comedi_dio_insn_config(dev, s, insn, data, mask); - if (ret) - return ret; - - subdev_8255_do_config(dev, s); - - return insn->n; -} - -static int __subdev_8255_init(struct comedi_device *dev, - struct comedi_subdevice *s, - int (*io)(struct comedi_device *dev, - int dir, int port, int data, - unsigned long regbase), - unsigned long regbase, - bool is_mmio) -{ - struct subdev_8255_private *spriv; - - spriv = comedi_alloc_spriv(s, sizeof(*spriv)); - if (!spriv) - return -ENOMEM; - - if (io) - spriv->io = io; - else if (is_mmio) - spriv->io = subdev_8255_mmio; - else - spriv->io = subdev_8255_io; - spriv->regbase = regbase; - - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 24; - s->range_table = &range_digital; - s->maxdata = 1; - s->insn_bits = subdev_8255_insn; - s->insn_config = subdev_8255_insn_config; - - subdev_8255_do_config(dev, s); - - return 0; -} - -/** - * subdev_8255_init - initialize DIO subdevice for driving I/O mapped 8255 - * @dev: comedi device owning subdevice - * @s: comedi subdevice to initialize - * @io: (optional) register I/O call-back function - * @regbase: offset of 8255 registers from dev->iobase, or call-back context - * - * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip. - * - * If the optional I/O call-back function is provided, its prototype is of - * the following form: - * - * int my_8255_callback(struct comedi_device *dev, int dir, int port, - * int data, unsigned long regbase); - * - * where 'dev', and 'regbase' match the values passed to this function, - * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir' - * is the direction (0 for read, 1 for write) and 'data' is the value to be - * written. It should return 0 if writing or the value read if reading. - * - * If the optional I/O call-back function is not provided, an internal - * call-back function is used which uses consecutive I/O port addresses - * starting at dev->iobase + regbase. - * - * Return: -ENOMEM if failed to allocate memory, zero on success. - */ -int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s, - int (*io)(struct comedi_device *dev, int dir, int port, - int data, unsigned long regbase), - unsigned long regbase) -{ - return __subdev_8255_init(dev, s, io, regbase, false); -} -EXPORT_SYMBOL_GPL(subdev_8255_init); - -/** - * subdev_8255_mm_init - initialize DIO subdevice for driving mmio-mapped 8255 - * @dev: comedi device owning subdevice - * @s: comedi subdevice to initialize - * @io: (optional) register I/O call-back function - * @regbase: offset of 8255 registers from dev->mmio, or call-back context - * - * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip. - * - * If the optional I/O call-back function is provided, its prototype is of - * the following form: - * - * int my_8255_callback(struct comedi_device *dev, int dir, int port, - * int data, unsigned long regbase); - * - * where 'dev', and 'regbase' match the values passed to this function, - * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir' - * is the direction (0 for read, 1 for write) and 'data' is the value to be - * written. It should return 0 if writing or the value read if reading. - * - * If the optional I/O call-back function is not provided, an internal - * call-back function is used which uses consecutive MMIO virtual addresses - * starting at dev->mmio + regbase. - * - * Return: -ENOMEM if failed to allocate memory, zero on success. - */ -int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s, - int (*io)(struct comedi_device *dev, int dir, int port, - int data, unsigned long regbase), - unsigned long regbase) -{ - return __subdev_8255_init(dev, s, io, regbase, true); -} -EXPORT_SYMBOL_GPL(subdev_8255_mm_init); - -/* - * Start of the 8255 standalone device - */ - static int dev_8255_attach(struct comedi_device *dev, struct comedi_devconfig *it) { @@ -324,14 +109,14 @@ static int dev_8255_attach(struct comedi_device *dev, static void dev_8255_detach(struct comedi_device *dev) { struct comedi_subdevice *s; - struct subdev_8255_private *spriv; int i; for (i = 0; i < dev->n_subdevices; i++) { s = &dev->subdevices[i]; if (s->type != COMEDI_SUBD_UNUSED) { - spriv = s->private; - release_region(spriv->regbase, I8255_SIZE); + unsigned long regbase = subdev_8255_regbase(s); + + release_region(regbase, I8255_SIZE); } } } @@ -345,5 +130,5 @@ static struct comedi_driver dev_8255_driver = { module_comedi_driver(dev_8255_driver); MODULE_AUTHOR("Comedi http://www.comedi.org"); -MODULE_DESCRIPTION("Comedi low-level driver"); +MODULE_DESCRIPTION("Comedi driver for standalone 8255 devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/staging/comedi/drivers/8255.h b/drivers/staging/comedi/drivers/8255.h index 21856d669288..41823de69b77 100644 --- a/drivers/staging/comedi/drivers/8255.h +++ b/drivers/staging/comedi/drivers/8255.h @@ -46,4 +46,6 @@ int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s, int data, unsigned long regbase), unsigned long regbase); +unsigned long subdev_8255_regbase(struct comedi_subdevice *s); + #endif diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile index d6d834006015..5764dc9a6893 100644 --- a/drivers/staging/comedi/drivers/Makefile +++ b/drivers/staging/comedi/drivers/Makefile @@ -139,7 +139,8 @@ obj-$(CONFIG_COMEDI_NI_TIOCMD) += ni_tiocmd.o obj-$(CONFIG_COMEDI_NI_LABPC) += ni_labpc_common.o obj-$(CONFIG_COMEDI_NI_LABPC_ISADMA) += ni_labpc_isadma.o -obj-$(CONFIG_COMEDI_8255) += 8255.o +obj-$(CONFIG_COMEDI_8255) += comedi_8255.o +obj-$(CONFIG_COMEDI_8255_SA) += 8255.o obj-$(CONFIG_COMEDI_AMPLC_DIO200) += amplc_dio200_common.o obj-$(CONFIG_COMEDI_AMPLC_PC236) += amplc_pc236_common.o obj-$(CONFIG_COMEDI_DAS08) += das08.o diff --git a/drivers/staging/comedi/drivers/comedi_8255.c b/drivers/staging/comedi/drivers/comedi_8255.c new file mode 100644 index 000000000000..b2441efc61cc --- /dev/null +++ b/drivers/staging/comedi/drivers/comedi_8255.c @@ -0,0 +1,285 @@ +/* + * comedi_8255.c + * Generic 8255 digital I/O support + * + * Split from the Comedi "8255" driver module. + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1998 David A. Schleef + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * Module: comedi_8255 + * Description: Generic 8255 support + * Author: ds + * Updated: Fri, 22 May 2015 12:14:17 +0000 + * Status: works + * + * This module is not used directly by end-users. Rather, it is used by + * other drivers to provide support for an 8255 "Programmable Peripheral + * Interface" (PPI) chip. + * + * The classic in digital I/O. The 8255 appears in Comedi as a single + * digital I/O subdevice with 24 channels. The channel 0 corresponds to + * the 8255's port A, bit 0; channel 23 corresponds to port C, bit 7. + * Direction configuration is done in blocks, with channels 0-7, 8-15, + * 16-19, and 20-23 making up the 4 blocks. The only 8255 mode + * supported is mode 0. + */ + +#include +#include "../comedidev.h" + +#include "8255.h" + +struct subdev_8255_private { + unsigned long regbase; + int (*io)(struct comedi_device *dev, int dir, int port, int data, + unsigned long regbase); +}; + +static int subdev_8255_io(struct comedi_device *dev, + int dir, int port, int data, unsigned long regbase) +{ + if (dir) { + outb(data, dev->iobase + regbase + port); + return 0; + } + return inb(dev->iobase + regbase + port); +} + +static int subdev_8255_mmio(struct comedi_device *dev, + int dir, int port, int data, unsigned long regbase) +{ + if (dir) { + writeb(data, dev->mmio + regbase + port); + return 0; + } + return readb(dev->mmio + regbase + port); +} + +static int subdev_8255_insn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct subdev_8255_private *spriv = s->private; + unsigned long regbase = spriv->regbase; + unsigned int mask; + unsigned int v; + + mask = comedi_dio_update_state(s, data); + if (mask) { + if (mask & 0xff) + spriv->io(dev, 1, I8255_DATA_A_REG, + s->state & 0xff, regbase); + if (mask & 0xff00) + spriv->io(dev, 1, I8255_DATA_B_REG, + (s->state >> 8) & 0xff, regbase); + if (mask & 0xff0000) + spriv->io(dev, 1, I8255_DATA_C_REG, + (s->state >> 16) & 0xff, regbase); + } + + v = spriv->io(dev, 0, I8255_DATA_A_REG, 0, regbase); + v |= (spriv->io(dev, 0, I8255_DATA_B_REG, 0, regbase) << 8); + v |= (spriv->io(dev, 0, I8255_DATA_C_REG, 0, regbase) << 16); + + data[1] = v; + + return insn->n; +} + +static void subdev_8255_do_config(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + struct subdev_8255_private *spriv = s->private; + unsigned long regbase = spriv->regbase; + int config; + + config = I8255_CTRL_CW; + /* 1 in io_bits indicates output, 1 in config indicates input */ + if (!(s->io_bits & 0x0000ff)) + config |= I8255_CTRL_A_IO; + if (!(s->io_bits & 0x00ff00)) + config |= I8255_CTRL_B_IO; + if (!(s->io_bits & 0x0f0000)) + config |= I8255_CTRL_C_LO_IO; + if (!(s->io_bits & 0xf00000)) + config |= I8255_CTRL_C_HI_IO; + + spriv->io(dev, 1, I8255_CTRL_REG, config, regbase); +} + +static int subdev_8255_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int mask; + int ret; + + if (chan < 8) + mask = 0x0000ff; + else if (chan < 16) + mask = 0x00ff00; + else if (chan < 20) + mask = 0x0f0000; + else + mask = 0xf00000; + + ret = comedi_dio_insn_config(dev, s, insn, data, mask); + if (ret) + return ret; + + subdev_8255_do_config(dev, s); + + return insn->n; +} + +static int __subdev_8255_init(struct comedi_device *dev, + struct comedi_subdevice *s, + int (*io)(struct comedi_device *dev, + int dir, int port, int data, + unsigned long regbase), + unsigned long regbase, + bool is_mmio) +{ + struct subdev_8255_private *spriv; + + spriv = comedi_alloc_spriv(s, sizeof(*spriv)); + if (!spriv) + return -ENOMEM; + + if (io) + spriv->io = io; + else if (is_mmio) + spriv->io = subdev_8255_mmio; + else + spriv->io = subdev_8255_io; + spriv->regbase = regbase; + + s->type = COMEDI_SUBD_DIO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = 24; + s->range_table = &range_digital; + s->maxdata = 1; + s->insn_bits = subdev_8255_insn; + s->insn_config = subdev_8255_insn_config; + + subdev_8255_do_config(dev, s); + + return 0; +} + +/** + * subdev_8255_init - initialize DIO subdevice for driving I/O mapped 8255 + * @dev: comedi device owning subdevice + * @s: comedi subdevice to initialize + * @io: (optional) register I/O call-back function + * @regbase: offset of 8255 registers from dev->iobase, or call-back context + * + * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip. + * + * If the optional I/O call-back function is provided, its prototype is of + * the following form: + * + * int my_8255_callback(struct comedi_device *dev, int dir, int port, + * int data, unsigned long regbase); + * + * where 'dev', and 'regbase' match the values passed to this function, + * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir' + * is the direction (0 for read, 1 for write) and 'data' is the value to be + * written. It should return 0 if writing or the value read if reading. + * + * If the optional I/O call-back function is not provided, an internal + * call-back function is used which uses consecutive I/O port addresses + * starting at dev->iobase + regbase. + * + * Return: -ENOMEM if failed to allocate memory, zero on success. + */ +int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s, + int (*io)(struct comedi_device *dev, int dir, int port, + int data, unsigned long regbase), + unsigned long regbase) +{ + return __subdev_8255_init(dev, s, io, regbase, false); +} +EXPORT_SYMBOL_GPL(subdev_8255_init); + +/** + * subdev_8255_mm_init - initialize DIO subdevice for driving mmio-mapped 8255 + * @dev: comedi device owning subdevice + * @s: comedi subdevice to initialize + * @io: (optional) register I/O call-back function + * @regbase: offset of 8255 registers from dev->mmio, or call-back context + * + * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip. + * + * If the optional I/O call-back function is provided, its prototype is of + * the following form: + * + * int my_8255_callback(struct comedi_device *dev, int dir, int port, + * int data, unsigned long regbase); + * + * where 'dev', and 'regbase' match the values passed to this function, + * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir' + * is the direction (0 for read, 1 for write) and 'data' is the value to be + * written. It should return 0 if writing or the value read if reading. + * + * If the optional I/O call-back function is not provided, an internal + * call-back function is used which uses consecutive MMIO virtual addresses + * starting at dev->mmio + regbase. + * + * Return: -ENOMEM if failed to allocate memory, zero on success. + */ +int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s, + int (*io)(struct comedi_device *dev, int dir, int port, + int data, unsigned long regbase), + unsigned long regbase) +{ + return __subdev_8255_init(dev, s, io, regbase, true); +} +EXPORT_SYMBOL_GPL(subdev_8255_mm_init); + +/** + * subdev_8255_regbase - get offset of 8255 registers or call-back context + * @s: comedi subdevice + * + * Returns the 'regbase' parameter that was previously passed to to + * subdev_8255_init() or subdev_8255_mm_init() to set up the subdevice. + * Only valid if the subdevice was set up successfully. + */ +unsigned long subdev_8255_regbase(struct comedi_subdevice *s) +{ + struct subdev_8255_private *spriv = s->private; + + return spriv->regbase; +} +EXPORT_SYMBOL_GPL(subdev_8255_regbase); + +static int __init comedi_8255_module_init(void) +{ + return 0; +} +module_init(comedi_8255_module_init); + +static void __exit comedi_8255_module_exit(void) +{ +} +module_exit(comedi_8255_module_exit); + +MODULE_AUTHOR("Comedi http://www.comedi.org"); +MODULE_DESCRIPTION("Comedi: Generic 8255 digital I/O support"); +MODULE_LICENSE("GPL"); -- 2.30.2