Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a213fa6 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman
Browse files

staging: comedi: amplc_dio200_common: convert driver to use the comedi_8254 module



Convert this driver to use the comedi_8254 module to provide the 8254 timer support.

Add 'clock_src' and 'gate_src' members to the comedi_8254 data for convienence.

Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 99ca4e46
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1275,6 +1275,7 @@ config COMEDI_KCOMEDILIB
	  called kcomedilib.

config COMEDI_AMPLC_DIO200
	select COMEDI_8254
	tristate

config COMEDI_AMPLC_PC236
+94 −201
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@

#include "amplc_dio200.h"
#include "comedi_fc.h"
#include "8253.h"
#include "comedi_8254.h"
#include "8255.h"		/* only for register defines */

/* 200 series registers */
@@ -97,12 +97,6 @@ static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
	1000000,		/* 1 millisecond. */
};

struct dio200_subdev_8254 {
	unsigned int ofs;		/* Counter base offset */
	unsigned int clock_src[3];	/* Current clock sources */
	unsigned int gate_src[3];	/* Current gate sources */
};

struct dio200_subdev_8255 {
	unsigned int ofs;		/* DIO base offset */
};
@@ -169,6 +163,27 @@ static void dio200_write32(struct comedi_device *dev,
		outl(val, dev->iobase + offset);
}

static unsigned int dio200_subdev_8254_offset(struct comedi_device *dev,
					      struct comedi_subdevice *s)
{
	const struct dio200_board *board = dev->board_ptr;
	struct comedi_8254 *i8254 = s->private;
	unsigned int offset;

	/* get the offset that was passed to comedi_8254_*_init() */
	if (dev->mmio)
		offset = i8254->mmio - dev->mmio;
	else
		offset = i8254->iobase - dev->iobase;

	/* remove the shift that was added for PCIe boards */
	if (board->is_pcie)
		offset >>= 3;

	/* this offset now works for the dio200_{read,write} helpers */
	return offset;
}

static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
					struct comedi_subdevice *s,
					struct comedi_insn *insn,
@@ -474,211 +489,71 @@ static irqreturn_t dio200_interrupt(int irq, void *d)
	return IRQ_RETVAL(handled);
}

static unsigned int dio200_subdev_8254_read_chan(struct comedi_device *dev,
						 struct comedi_subdevice *s,
						 unsigned int chan)
{
	struct dio200_subdev_8254 *subpriv = s->private;
	unsigned int val;

	/* latch counter */
	val = chan << 6;
	dio200_write8(dev, subpriv->ofs + i8254_control_reg, val);
	/* read lsb, msb */
	val = dio200_read8(dev, subpriv->ofs + chan);
	val += dio200_read8(dev, subpriv->ofs + chan) << 8;
	return val;
}

static void dio200_subdev_8254_write_chan(struct comedi_device *dev,
static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
					    struct comedi_subdevice *s,
					    unsigned int chan,
					  unsigned int count)
					    unsigned int src)
{
	struct dio200_subdev_8254 *subpriv = s->private;
	unsigned int offset = dio200_subdev_8254_offset(dev, s);

	/* write lsb, msb */
	dio200_write8(dev, subpriv->ofs + chan, count & 0xff);
	dio200_write8(dev, subpriv->ofs + chan, (count >> 8) & 0xff);
	dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
		      gat_sce((offset >> 2) & 1, chan, src));
}

static void dio200_subdev_8254_set_mode(struct comedi_device *dev,
static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
					     struct comedi_subdevice *s,
					     unsigned int chan,
					unsigned int mode)
{
	struct dio200_subdev_8254 *subpriv = s->private;
	unsigned int byte;

	byte = chan << 6;
	byte |= 0x30;		/* access order: lsb, msb */
	byte |= (mode & 0xf);	/* counter mode and BCD|binary */
	dio200_write8(dev, subpriv->ofs + i8254_control_reg, byte);
}

static unsigned int dio200_subdev_8254_status(struct comedi_device *dev,
					      struct comedi_subdevice *s,
					      unsigned int chan)
{
	struct dio200_subdev_8254 *subpriv = s->private;

	/* latch status */
	dio200_write8(dev, subpriv->ofs + i8254_control_reg,
		      0xe0 | (2 << chan));
	/* read status */
	return dio200_read8(dev, subpriv->ofs + chan);
}

static int dio200_subdev_8254_read(struct comedi_device *dev,
				   struct comedi_subdevice *s,
				   struct comedi_insn *insn,
				   unsigned int *data)
					     unsigned int src)
{
	int chan = CR_CHAN(insn->chanspec);
	unsigned int n;

	for (n = 0; n < insn->n; n++)
		data[n] = dio200_subdev_8254_read_chan(dev, s, chan);
	unsigned int offset = dio200_subdev_8254_offset(dev, s);

	return insn->n;
	dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
		      clk_sce((offset >> 2) & 1, chan, src));
}

static int dio200_subdev_8254_write(struct comedi_device *dev,
static int dio200_subdev_8254_config(struct comedi_device *dev,
				     struct comedi_subdevice *s,
				     struct comedi_insn *insn,
				     unsigned int *data)
{
	int chan = CR_CHAN(insn->chanspec);
	unsigned int n;

	for (n = 0; n < insn->n; n++)
		dio200_subdev_8254_write_chan(dev, s, chan, data[n]);

	return insn->n;
}

static int dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
					   struct comedi_subdevice *s,
					   unsigned int counter_number,
					   unsigned int gate_src)
{
	const struct dio200_board *board = dev->board_ptr;
	struct dio200_subdev_8254 *subpriv = s->private;
	unsigned char byte;

	if (!board->has_clk_gat_sce)
		return -1;
	if (gate_src > (board->is_pcie ? 31 : 7))
		return -1;

	subpriv->gate_src[counter_number] = gate_src;
	byte = gat_sce((subpriv->ofs >> 2) & 1, counter_number, gate_src);
	dio200_write8(dev, DIO200_GAT_SCE(subpriv->ofs >> 3), byte);

	return 0;
}

static int dio200_subdev_8254_get_gate_src(struct comedi_device *dev,
					   struct comedi_subdevice *s,
					   unsigned int counter_number)
{
	const struct dio200_board *board = dev->board_ptr;
	struct dio200_subdev_8254 *subpriv = s->private;

	if (!board->has_clk_gat_sce)
		return -1;

	return subpriv->gate_src[counter_number];
}

static int dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
					    struct comedi_subdevice *s,
					    unsigned int counter_number,
					    unsigned int clock_src)
{
	const struct dio200_board *board = dev->board_ptr;
	struct dio200_subdev_8254 *subpriv = s->private;
	unsigned char byte;

	if (!board->has_clk_gat_sce)
		return -1;
	if (clock_src > (board->is_pcie ? 31 : 7))
		return -1;

	subpriv->clock_src[counter_number] = clock_src;
	byte = clk_sce((subpriv->ofs >> 2) & 1, counter_number, clock_src);
	dio200_write8(dev, DIO200_CLK_SCE(subpriv->ofs >> 3), byte);

	return 0;
}

static int dio200_subdev_8254_get_clock_src(struct comedi_device *dev,
					    struct comedi_subdevice *s,
					    unsigned int counter_number,
					    unsigned int *period_ns)
{
	const struct dio200_board *board = dev->board_ptr;
	struct dio200_subdev_8254 *subpriv = s->private;
	unsigned clock_src;
	struct comedi_8254 *i8254 = s->private;
	unsigned int chan = CR_CHAN(insn->chanspec);
	unsigned int max_src = board->is_pcie ? 31 : 7;
	unsigned int src;

	if (!board->has_clk_gat_sce)
		return -1;

	clock_src = subpriv->clock_src[counter_number];
	*period_ns = clock_period[clock_src];
	return clock_src;
}

static int dio200_subdev_8254_config(struct comedi_device *dev,
				     struct comedi_subdevice *s,
				     struct comedi_insn *insn,
				     unsigned int *data)
{
	int ret = 0;
	int chan = CR_CHAN(insn->chanspec);
		return -EINVAL;

	switch (data[0]) {
	case INSN_CONFIG_SET_COUNTER_MODE:
		if (data[1] > (I8254_MODE5 | I8254_BCD))
			ret = -EINVAL;
		else
			dio200_subdev_8254_set_mode(dev, s, chan, data[1]);
		break;
	case INSN_CONFIG_8254_READ_STATUS:
		data[1] = dio200_subdev_8254_status(dev, s, chan);
		break;
	case INSN_CONFIG_SET_GATE_SRC:
		ret = dio200_subdev_8254_set_gate_src(dev, s, chan, data[2]);
		if (ret < 0)
			ret = -EINVAL;
		src = data[2];
		if (src > max_src)
			return -EINVAL;

		dio200_subdev_8254_set_gate_src(dev, s, chan, src);
		i8254->gate_src[chan] = src;
		break;
	case INSN_CONFIG_GET_GATE_SRC:
		ret = dio200_subdev_8254_get_gate_src(dev, s, chan);
		if (ret < 0) {
			ret = -EINVAL;
			break;
		}
		data[2] = ret;
		data[2] = i8254->gate_src[chan];
		break;
	case INSN_CONFIG_SET_CLOCK_SRC:
		ret = dio200_subdev_8254_set_clock_src(dev, s, chan, data[1]);
		if (ret < 0)
			ret = -EINVAL;
		src = data[1];
		if (src > max_src)
			return -EINVAL;

		dio200_subdev_8254_set_clock_src(dev, s, chan, src);
		i8254->clock_src[chan] = src;
		break;
	case INSN_CONFIG_GET_CLOCK_SRC:
		ret = dio200_subdev_8254_get_clock_src(dev, s, chan, &data[2]);
		if (ret < 0) {
			ret = -EINVAL;
			break;
		}
		data[1] = ret;
		data[1] = i8254->clock_src[chan];
		data[2] = clock_period[i8254->clock_src[chan]];
		break;
	default:
		ret = -EINVAL;
		break;
		return -EINVAL;
	}

	return ret < 0 ? ret : insn->n;
	return insn->n;
}

static int dio200_subdev_8254_init(struct comedi_device *dev,
@@ -686,28 +561,46 @@ static int dio200_subdev_8254_init(struct comedi_device *dev,
				   unsigned int offset)
{
	const struct dio200_board *board = dev->board_ptr;
	struct dio200_subdev_8254 *subpriv;
	unsigned int chan;
	struct comedi_8254 *i8254;
	unsigned int regshift;
	int chan;

	subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
	if (!subpriv)
	/*
	 * PCIe boards need the offset shifted in order to get the
	 * correct base address of the timer.
	 */
	if (board->is_pcie) {
		offset <<= 3;
		regshift = 3;
	} else {
		regshift = 0;
	}

	if (dev->mmio)
		i8254 = comedi_8254_mm_init(dev->mmio + offset,
					    0, I8254_IO8, regshift);
	else
		i8254 = comedi_8254_init(dev->iobase + offset,
					 0, I8254_IO8, regshift);
	if (!i8254)
		return -ENOMEM;

	s->type = COMEDI_SUBD_COUNTER;
	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
	s->n_chan = 3;
	s->maxdata = 0xFFFF;
	s->insn_read = dio200_subdev_8254_read;
	s->insn_write = dio200_subdev_8254_write;
	s->insn_config = dio200_subdev_8254_config;
	comedi_8254_subdevice_init(s, i8254);

	subpriv->ofs = offset;
	i8254->insn_config = dio200_subdev_8254_config;

	/*
	 * There could be multiple timers so this driver does not
	 * use dev->pacer to save the i8254 pointer. Instead,
	 * comedi_8254_subdevice_init() saved the i8254 pointer in
	 * s->private. Set the runflag bit so that the core will
	 * automatically free it when the driver is detached.
	 */
	s->runflags |= COMEDI_SRF_FREE_SPRIV;

	/* Initialize channels. */
	for (chan = 0; chan < 3; chan++) {
		dio200_subdev_8254_set_mode(dev, s, chan,
					    I8254_MODE0 | I8254_BINARY);
	if (board->has_clk_gat_sce) {
		for (chan = 0; chan < 3; chan++) {
			/* Gate source 0 is VCC (logic 1). */
			dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
			/* Clock source 0 is the dedicated clock input. */
+4 −0
Original line number Diff line number Diff line
@@ -71,6 +71,8 @@
 * #next_div:		next divisor for single counter
 * @next_div1:		next divisor to use for first cascaded counter
 * @next_div2:		next divisor to use for second cascaded counter
 * @clock_src;		current clock source for each counter (driver specific)
 * @gate_src;		current gate source  for each counter (driver specific)
 * @busy:		flags used to indicate that a counter is "busy"
 * @insn_config:	driver specific (*insn_config) callback
 */
@@ -86,6 +88,8 @@ struct comedi_8254 {
	unsigned int next_div;
	unsigned int next_div1;
	unsigned int next_div2;
	unsigned int clock_src[3];
	unsigned int gate_src[3];
	bool busy[3];

	int (*insn_config)(struct comedi_device *, struct comedi_subdevice *s,