Commit 18cbf973 authored by Ian Abbott's avatar Ian Abbott Committed by Greg Kroah-Hartman
Browse files

staging: comedi: amplc_dio200: implement timer subdevice



Implement the timer subdevice for the new PCIe boards.  The subdevice was
previously marked as unused, but was reserved for this purpose.

Signed-off-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 87276012
Loading
Loading
Loading
Loading
+144 −1
Original line number Diff line number Diff line
@@ -301,6 +301,8 @@
/* Extra registers for new PCIe boards */
#define DIO200_ENHANCE		0x20	/* 1 to enable enhanced features */
#define DIO200_VERSION		0x24	/* Hardware version register */
#define DIO200_TS_CONFIG	0x600	/* Timestamp timer config register */
#define DIO200_TS_COUNT		0x602	/* Timestamp timer count register */

/*
 * Functions for constructing value for DIO_200_?CLK_SCE and
@@ -342,6 +344,22 @@ static const unsigned int clock_period[32] = {
	/* clock sources 12 and later reserved for enhanced boards */
};

/*
 * Timestamp timer configuration register (for new PCIe boards).
 */
#define TS_CONFIG_RESET		0x100	/* Reset counter to zero. */
#define TS_CONFIG_CLK_SRC_MASK	0x0FF	/* Clock source. */
#define TS_CONFIG_MAX_CLK_SRC	2	/* Maximum clock source value. */

/*
 * Periods of the timestamp timer clock sources in nanoseconds.
 */
static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
	1,			/* 1 nanosecond (but with 20 ns granularity). */
	1000,			/* 1 microsecond. */
	1000000,		/* 1 millisecond. */
};

/*
 * Register region.
 */
@@ -1621,6 +1639,118 @@ static void dio200_subdev_8255_cleanup(struct comedi_device *dev,
	kfree(subpriv);
}

/*
 * Handle 'insn_read' for a timer subdevice.
 */
static int dio200_subdev_timer_read(struct comedi_device *dev,
				    struct comedi_subdevice *s,
				    struct comedi_insn *insn,
				    unsigned int *data)
{
	unsigned int n;

	for (n = 0; n < insn->n; n++)
		data[n] = dio200_read32(dev, DIO200_TS_COUNT);
	return n;
}

/*
 * Reset timer subdevice.
 */
static void dio200_subdev_timer_reset(struct comedi_device *dev,
				      struct comedi_subdevice *s)
{
	unsigned int clock;

	clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
	dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET);
	dio200_write32(dev, DIO200_TS_CONFIG, clock);
}

/*
 * Get timer subdevice clock source and period.
 */
static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev,
					      struct comedi_subdevice *s,
					      unsigned int *src,
					      unsigned int *period)
{
	unsigned int clk;

	clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
	*src = clk;
	*period = (clk < ARRAY_SIZE(ts_clock_period)) ?
		  ts_clock_period[clk] : 0;
}

/*
 * Set timer subdevice clock source.
 */
static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev,
					     struct comedi_subdevice *s,
					     unsigned int src)
{
	if (src > TS_CONFIG_MAX_CLK_SRC)
		return -EINVAL;
	dio200_write32(dev, DIO200_TS_CONFIG, src);
	return 0;
}

/*
 * Handle 'insn_config' for a timer subdevice.
 */
static int dio200_subdev_timer_config(struct comedi_device *dev,
				      struct comedi_subdevice *s,
				      struct comedi_insn *insn,
				      unsigned int *data)
{
	int ret = 0;

	switch (data[0]) {
	case INSN_CONFIG_RESET:
		dio200_subdev_timer_reset(dev, s);
		break;
	case INSN_CONFIG_SET_CLOCK_SRC:
		ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]);
		if (ret < 0)
			ret = -EINVAL;
		break;
	case INSN_CONFIG_GET_CLOCK_SRC:
		dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]);
		break;
	default:
		ret = -EINVAL;
		break;
	}
	return ret < 0 ? ret : insn->n;
}

/*
 * This function initializes a timer subdevice.
 *
 * Uses the timestamp timer registers.  There is only one timestamp timer.
 */
static int dio200_subdev_timer_init(struct comedi_device *dev,
				    struct comedi_subdevice *s)
{
	s->type = COMEDI_SUBD_TIMER;
	s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
	s->n_chan = 1;
	s->maxdata = 0xFFFFFFFF;
	s->insn_read = dio200_subdev_timer_read;
	s->insn_config = dio200_subdev_timer_config;
	return 0;
}

/*
 * This function cleans up a timer subdevice.
 */
static void dio200_subdev_timer_cleanup(struct comedi_device *dev,
					struct comedi_subdevice *s)
{
	/* Nothing to do. */
}

/*
 * This function does some special set-up for the PCIe boards
 * PCIe215, PCIe236, PCIe296.
@@ -1735,7 +1865,15 @@ static int dio200_common_attach(struct comedi_device *dev, unsigned int irq,
			}
			break;
		case sd_timer:
			/* TODO.  Fall-thru to default for now. */
			/* Only on PCIe boards. */
			if (DO_PCI) {
				ret = dio200_subdev_timer_init(dev, s);
				if (ret < 0)
					return ret;
			} else {
				s->type = COMEDI_SUBD_UNUSED;
			}
			break;
		default:
			s->type = COMEDI_SUBD_UNUSED;
			break;
@@ -1897,6 +2035,11 @@ static void dio200_detach(struct comedi_device *dev)
			case sd_intr:
				dio200_subdev_intr_cleanup(dev, s);
				break;
			case sd_timer:
				/* Only on PCIe boards. */
				if (DO_PCI)
					dio200_subdev_timer_cleanup(dev, s);
				break;
			default:
				break;
			}