summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/comedi_8254.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/comedi_8254.c')
-rw-r--r--drivers/staging/comedi/drivers/comedi_8254.c655
1 files changed, 0 insertions, 655 deletions
diff --git a/drivers/staging/comedi/drivers/comedi_8254.c b/drivers/staging/comedi/drivers/comedi_8254.c
deleted file mode 100644
index d1d509e9add9..000000000000
--- a/drivers/staging/comedi/drivers/comedi_8254.c
+++ /dev/null
@@ -1,655 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * comedi_8254.c
- * Generic 8254 timer/counter support
- * Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
- *
- * Based on 8253.h and various subdevice implementations in comedi drivers.
- *
- * COMEDI - Linux Control and Measurement Device Interface
- * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
- */
-
-/*
- * Module: comedi_8254
- * Description: Generic 8254 timer/counter support
- * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
- * Updated: Thu Jan 8 16:45:45 MST 2015
- * Status: works
- *
- * This module is not used directly by end-users. Rather, it is used by other
- * drivers to provide support for an 8254 Programmable Interval Timer. These
- * counters are typically used to generate the pacer clock used for data
- * acquisition. Some drivers also expose the counters for general purpose use.
- *
- * This module provides the following basic functions:
- *
- * comedi_8254_init() / comedi_8254_mm_init()
- * Initializes this module to access the 8254 registers. The _mm version
- * sets up the module for MMIO register access the other for PIO access.
- * The pointer returned from these functions is normally stored in the
- * comedi_device dev->pacer and will be freed by the comedi core during
- * the driver (*detach). If a driver has multiple 8254 devices, they need
- * to be stored in the drivers private data and freed when the driver is
- * detached.
- *
- * NOTE: The counters are reset by setting them to I8254_MODE0 as part of
- * this initialization.
- *
- * comedi_8254_set_mode()
- * Sets a counters operation mode:
- * I8254_MODE0 Interrupt on terminal count
- * I8254_MODE1 Hardware retriggerable one-shot
- * I8254_MODE2 Rate generator
- * I8254_MODE3 Square wave mode
- * I8254_MODE4 Software triggered strobe
- * I8254_MODE5 Hardware triggered strobe (retriggerable)
- *
- * In addition I8254_BCD and I8254_BINARY specify the counting mode:
- * I8254_BCD BCD counting
- * I8254_BINARY Binary counting
- *
- * comedi_8254_write()
- * Writes an initial value to a counter.
- *
- * The largest possible initial count is 0; this is equivalent to 2^16
- * for binary counting and 10^4 for BCD counting.
- *
- * NOTE: The counter does not stop when it reaches zero. In Mode 0, 1, 4,
- * and 5 the counter "wraps around" to the highest count, either 0xffff
- * for binary counting or 9999 for BCD counting, and continues counting.
- * Modes 2 and 3 are periodic; the counter reloads itself with the initial
- * count and continues counting from there.
- *
- * comedi_8254_read()
- * Reads the current value from a counter.
- *
- * comedi_8254_status()
- * Reads the status of a counter.
- *
- * comedi_8254_load()
- * Sets a counters operation mode and writes the initial value.
- *
- * Typically the pacer clock is created by cascading two of the 16-bit counters
- * to create a 32-bit rate generator (I8254_MODE2). These functions are
- * provided to handle the cascaded counters:
- *
- * comedi_8254_ns_to_timer()
- * Calculates the divisor value needed for a single counter to generate
- * ns timing.
- *
- * comedi_8254_cascade_ns_to_timer()
- * Calculates the two divisor values needed to the generate the pacer
- * clock (in ns).
- *
- * comedi_8254_update_divisors()
- * Transfers the intermediate divisor values to the current divisors.
- *
- * comedi_8254_pacer_enable()
- * Programs the mode of the cascaded counters and writes the current
- * divisor values.
- *
- * To expose the counters as a subdevice for general purpose use the following
- * functions a provided:
- *
- * comedi_8254_subdevice_init()
- * Initializes a comedi_subdevice to use the 8254 timer.
- *
- * comedi_8254_set_busy()
- * Internally flags a counter as "busy". This is done to protect the
- * counters that are used for the cascaded 32-bit pacer.
- *
- * The subdevice provides (*insn_read) and (*insn_write) operations to read
- * the current value and write an initial value to a counter. A (*insn_config)
- * operation is also provided to handle the following comedi instructions:
- *
- * INSN_CONFIG_SET_COUNTER_MODE calls comedi_8254_set_mode()
- * INSN_CONFIG_8254_READ_STATUS calls comedi_8254_status()
- *
- * The (*insn_config) member of comedi_8254 can be initialized by the external
- * driver to handle any additional instructions.
- *
- * NOTE: Gate control, clock routing, and any interrupt handling for the
- * counters is not handled by this module. These features are driver dependent.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-
-#include "../comedidev.h"
-
-#include "comedi_8254.h"
-
-static unsigned int __i8254_read(struct comedi_8254 *i8254, unsigned int reg)
-{
- unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
- unsigned int val;
-
- switch (i8254->iosize) {
- default:
- case I8254_IO8:
- if (i8254->mmio)
- val = readb(i8254->mmio + reg_offset);
- else
- val = inb(i8254->iobase + reg_offset);
- break;
- case I8254_IO16:
- if (i8254->mmio)
- val = readw(i8254->mmio + reg_offset);
- else
- val = inw(i8254->iobase + reg_offset);
- break;
- case I8254_IO32:
- if (i8254->mmio)
- val = readl(i8254->mmio + reg_offset);
- else
- val = inl(i8254->iobase + reg_offset);
- break;
- }
- return val & 0xff;
-}
-
-static void __i8254_write(struct comedi_8254 *i8254,
- unsigned int val, unsigned int reg)
-{
- unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
-
- switch (i8254->iosize) {
- default:
- case I8254_IO8:
- if (i8254->mmio)
- writeb(val, i8254->mmio + reg_offset);
- else
- outb(val, i8254->iobase + reg_offset);
- break;
- case I8254_IO16:
- if (i8254->mmio)
- writew(val, i8254->mmio + reg_offset);
- else
- outw(val, i8254->iobase + reg_offset);
- break;
- case I8254_IO32:
- if (i8254->mmio)
- writel(val, i8254->mmio + reg_offset);
- else
- outl(val, i8254->iobase + reg_offset);
- break;
- }
-}
-
-/**
- * comedi_8254_status - return the status of a counter
- * @i8254: comedi_8254 struct for the timer
- * @counter: the counter number
- */
-unsigned int comedi_8254_status(struct comedi_8254 *i8254, unsigned int counter)
-{
- unsigned int cmd;
-
- if (counter > 2)
- return 0;
-
- cmd = I8254_CTRL_READBACK_STATUS | I8254_CTRL_READBACK_SEL_CTR(counter);
- __i8254_write(i8254, cmd, I8254_CTRL_REG);
-
- return __i8254_read(i8254, counter);
-}
-EXPORT_SYMBOL_GPL(comedi_8254_status);
-
-/**
- * comedi_8254_read - read the current counter value
- * @i8254: comedi_8254 struct for the timer
- * @counter: the counter number
- */
-unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter)
-{
- unsigned int val;
-
- if (counter > 2)
- return 0;
-
- /* latch counter */
- __i8254_write(i8254, I8254_CTRL_SEL_CTR(counter) | I8254_CTRL_LATCH,
- I8254_CTRL_REG);
-
- /* read LSB then MSB */
- val = __i8254_read(i8254, counter);
- val |= (__i8254_read(i8254, counter) << 8);
-
- return val;
-}
-EXPORT_SYMBOL_GPL(comedi_8254_read);
-
-/**
- * comedi_8254_write - load a 16-bit initial counter value
- * @i8254: comedi_8254 struct for the timer
- * @counter: the counter number
- * @val: the initial value
- */
-void comedi_8254_write(struct comedi_8254 *i8254,
- unsigned int counter, unsigned int val)
-{
- unsigned int byte;
-
- if (counter > 2)
- return;
- if (val > 0xffff)
- return;
-
- /* load LSB then MSB */
- byte = val & 0xff;
- __i8254_write(i8254, byte, counter);
- byte = (val >> 8) & 0xff;
- __i8254_write(i8254, byte, counter);
-}
-EXPORT_SYMBOL_GPL(comedi_8254_write);
-
-/**
- * comedi_8254_set_mode - set the mode of a counter
- * @i8254: comedi_8254 struct for the timer
- * @counter: the counter number
- * @mode: the I8254_MODEx and I8254_BCD|I8254_BINARY
- */
-int comedi_8254_set_mode(struct comedi_8254 *i8254, unsigned int counter,
- unsigned int mode)
-{
- unsigned int byte;
-
- if (counter > 2)
- return -EINVAL;
- if (mode > (I8254_MODE5 | I8254_BCD))
- return -EINVAL;
-
- byte = I8254_CTRL_SEL_CTR(counter) | /* select counter */
- I8254_CTRL_LSB_MSB | /* load LSB then MSB */
- mode; /* mode and BCD|binary */
- __i8254_write(i8254, byte, I8254_CTRL_REG);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(comedi_8254_set_mode);
-
-/**
- * comedi_8254_load - program the mode and initial count of a counter
- * @i8254: comedi_8254 struct for the timer
- * @counter: the counter number
- * @mode: the I8254_MODEx and I8254_BCD|I8254_BINARY
- * @val: the initial value
- */
-int comedi_8254_load(struct comedi_8254 *i8254, unsigned int counter,
- unsigned int val, unsigned int mode)
-{
- if (counter > 2)
- return -EINVAL;
- if (val > 0xffff)
- return -EINVAL;
- if (mode > (I8254_MODE5 | I8254_BCD))
- return -EINVAL;
-
- comedi_8254_set_mode(i8254, counter, mode);
- comedi_8254_write(i8254, counter, val);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(comedi_8254_load);
-
-/**
- * comedi_8254_pacer_enable - set the mode and load the cascaded counters
- * @i8254: comedi_8254 struct for the timer
- * @counter1: the counter number for the first divisor
- * @counter2: the counter number for the second divisor
- * @enable: flag to enable (load) the counters
- */
-void comedi_8254_pacer_enable(struct comedi_8254 *i8254,
- unsigned int counter1,
- unsigned int counter2,
- bool enable)
-{
- unsigned int mode;
-
- if (counter1 > 2 || counter2 > 2 || counter1 == counter2)
- return;
-
- if (enable)
- mode = I8254_MODE2 | I8254_BINARY;
- else
- mode = I8254_MODE0 | I8254_BINARY;
-
- comedi_8254_set_mode(i8254, counter1, mode);
- comedi_8254_set_mode(i8254, counter2, mode);
-
- if (enable) {
- /*
- * Divisors are loaded second counter then first counter to
- * avoid possible issues with the first counter expiring
- * before the second counter is loaded.
- */
- comedi_8254_write(i8254, counter2, i8254->divisor2);
- comedi_8254_write(i8254, counter1, i8254->divisor1);
- }
-}
-EXPORT_SYMBOL_GPL(comedi_8254_pacer_enable);
-
-/**
- * comedi_8254_update_divisors - update the divisors for the cascaded counters
- * @i8254: comedi_8254 struct for the timer
- */
-void comedi_8254_update_divisors(struct comedi_8254 *i8254)
-{
- /* masking is done since counter maps zero to 0x10000 */
- i8254->divisor = i8254->next_div & 0xffff;
- i8254->divisor1 = i8254->next_div1 & 0xffff;
- i8254->divisor2 = i8254->next_div2 & 0xffff;
-}
-EXPORT_SYMBOL_GPL(comedi_8254_update_divisors);
-
-/**
- * comedi_8254_cascade_ns_to_timer - calculate the cascaded divisor values
- * @i8254: comedi_8254 struct for the timer
- * @nanosec: the desired ns time
- * @flags: comedi_cmd flags
- */
-void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254,
- unsigned int *nanosec,
- unsigned int flags)
-{
- unsigned int d1 = i8254->next_div1 ? i8254->next_div1 : I8254_MAX_COUNT;
- unsigned int d2 = i8254->next_div2 ? i8254->next_div2 : I8254_MAX_COUNT;
- unsigned int div = d1 * d2;
- unsigned int ns_lub = 0xffffffff;
- unsigned int ns_glb = 0;
- unsigned int d1_lub = 0;
- unsigned int d1_glb = 0;
- unsigned int d2_lub = 0;
- unsigned int d2_glb = 0;
- unsigned int start;
- unsigned int ns;
- unsigned int ns_low;
- unsigned int ns_high;
-
- /* exit early if everything is already correct */
- if (div * i8254->osc_base == *nanosec &&
- d1 > 1 && d1 <= I8254_MAX_COUNT &&
- d2 > 1 && d2 <= I8254_MAX_COUNT &&
- /* check for overflow */
- div > d1 && div > d2 &&
- div * i8254->osc_base > div &&
- div * i8254->osc_base > i8254->osc_base)
- return;
-
- div = *nanosec / i8254->osc_base;
- d2 = I8254_MAX_COUNT;
- start = div / d2;
- if (start < 2)
- start = 2;
- for (d1 = start; d1 <= div / d1 + 1 && d1 <= I8254_MAX_COUNT; d1++) {
- for (d2 = div / d1;
- d1 * d2 <= div + d1 + 1 && d2 <= I8254_MAX_COUNT; d2++) {
- ns = i8254->osc_base * d1 * d2;
- if (ns <= *nanosec && ns > ns_glb) {
- ns_glb = ns;
- d1_glb = d1;
- d2_glb = d2;
- }
- if (ns >= *nanosec && ns < ns_lub) {
- ns_lub = ns;
- d1_lub = d1;
- d2_lub = d2;
- }
- }
- }
-
- switch (flags & CMDF_ROUND_MASK) {
- case CMDF_ROUND_NEAREST:
- default:
- ns_high = d1_lub * d2_lub * i8254->osc_base;
- ns_low = d1_glb * d2_glb * i8254->osc_base;
- if (ns_high - *nanosec < *nanosec - ns_low) {
- d1 = d1_lub;
- d2 = d2_lub;
- } else {
- d1 = d1_glb;
- d2 = d2_glb;
- }
- break;
- case CMDF_ROUND_UP:
- d1 = d1_lub;
- d2 = d2_lub;
- break;
- case CMDF_ROUND_DOWN:
- d1 = d1_glb;
- d2 = d2_glb;
- break;
- }
-
- *nanosec = d1 * d2 * i8254->osc_base;
- i8254->next_div1 = d1;
- i8254->next_div2 = d2;
-}
-EXPORT_SYMBOL_GPL(comedi_8254_cascade_ns_to_timer);
-
-/**
- * comedi_8254_ns_to_timer - calculate the divisor value for nanosec timing
- * @i8254: comedi_8254 struct for the timer
- * @nanosec: the desired ns time
- * @flags: comedi_cmd flags
- */
-void comedi_8254_ns_to_timer(struct comedi_8254 *i8254,
- unsigned int *nanosec, unsigned int flags)
-{
- unsigned int divisor;
-
- switch (flags & CMDF_ROUND_MASK) {
- default:
- case CMDF_ROUND_NEAREST:
- divisor = DIV_ROUND_CLOSEST(*nanosec, i8254->osc_base);
- break;
- case CMDF_ROUND_UP:
- divisor = DIV_ROUND_UP(*nanosec, i8254->osc_base);
- break;
- case CMDF_ROUND_DOWN:
- divisor = *nanosec / i8254->osc_base;
- break;
- }
- if (divisor < 2)
- divisor = 2;
- if (divisor > I8254_MAX_COUNT)
- divisor = I8254_MAX_COUNT;
-
- *nanosec = divisor * i8254->osc_base;
- i8254->next_div = divisor;
-}
-EXPORT_SYMBOL_GPL(comedi_8254_ns_to_timer);
-
-/**
- * comedi_8254_set_busy - set/clear the "busy" flag for a given counter
- * @i8254: comedi_8254 struct for the timer
- * @counter: the counter number
- * @busy: set/clear flag
- */
-void comedi_8254_set_busy(struct comedi_8254 *i8254,
- unsigned int counter, bool busy)
-{
- if (counter < 3)
- i8254->busy[counter] = busy;
-}
-EXPORT_SYMBOL_GPL(comedi_8254_set_busy);
-
-static int comedi_8254_insn_read(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- struct comedi_8254 *i8254 = s->private;
- unsigned int chan = CR_CHAN(insn->chanspec);
- int i;
-
- if (i8254->busy[chan])
- return -EBUSY;
-
- for (i = 0; i < insn->n; i++)
- data[i] = comedi_8254_read(i8254, chan);
-
- return insn->n;
-}
-
-static int comedi_8254_insn_write(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- struct comedi_8254 *i8254 = s->private;
- unsigned int chan = CR_CHAN(insn->chanspec);
-
- if (i8254->busy[chan])
- return -EBUSY;
-
- if (insn->n)
- comedi_8254_write(i8254, chan, data[insn->n - 1]);
-
- return insn->n;
-}
-
-static int comedi_8254_insn_config(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- struct comedi_8254 *i8254 = s->private;
- unsigned int chan = CR_CHAN(insn->chanspec);
- int ret;
-
- if (i8254->busy[chan])
- return -EBUSY;
-
- switch (data[0]) {
- case INSN_CONFIG_RESET:
- ret = comedi_8254_set_mode(i8254, chan,
- I8254_MODE0 | I8254_BINARY);
- if (ret)
- return ret;
- break;
- case INSN_CONFIG_SET_COUNTER_MODE:
- ret = comedi_8254_set_mode(i8254, chan, data[1]);
- if (ret)
- return ret;
- break;
- case INSN_CONFIG_8254_READ_STATUS:
- data[1] = comedi_8254_status(i8254, chan);
- break;
- default:
- /*
- * If available, call the driver provided (*insn_config)
- * to handle any driver implemented instructions.
- */
- if (i8254->insn_config)
- return i8254->insn_config(dev, s, insn, data);
-
- return -EINVAL;
- }
-
- return insn->n;
-}
-
-/**
- * comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer
- * @s: comedi_subdevice struct
- */
-void comedi_8254_subdevice_init(struct comedi_subdevice *s,
- struct comedi_8254 *i8254)
-{
- s->type = COMEDI_SUBD_COUNTER;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 3;
- s->maxdata = 0xffff;
- s->range_table = &range_unknown;
- s->insn_read = comedi_8254_insn_read;
- s->insn_write = comedi_8254_insn_write;
- s->insn_config = comedi_8254_insn_config;
-
- s->private = i8254;
-}
-EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init);
-
-static struct comedi_8254 *__i8254_init(unsigned long iobase,
- void __iomem *mmio,
- unsigned int osc_base,
- unsigned int iosize,
- unsigned int regshift)
-{
- struct comedi_8254 *i8254;
- int i;
-
- /* sanity check that the iosize is valid */
- if (!(iosize == I8254_IO8 || iosize == I8254_IO16 ||
- iosize == I8254_IO32))
- return NULL;
-
- i8254 = kzalloc(sizeof(*i8254), GFP_KERNEL);
- if (!i8254)
- return NULL;
-
- i8254->iobase = iobase;
- i8254->mmio = mmio;
- i8254->iosize = iosize;
- i8254->regshift = regshift;
-
- /* default osc_base to the max speed of a generic 8254 timer */
- i8254->osc_base = osc_base ? osc_base : I8254_OSC_BASE_10MHZ;
-
- /* reset all the counters by setting them to I8254_MODE0 */
- for (i = 0; i < 3; i++)
- comedi_8254_set_mode(i8254, i, I8254_MODE0 | I8254_BINARY);
-
- return i8254;
-}
-
-/**
- * comedi_8254_init - allocate and initialize the 8254 device for pio access
- * @mmio: port I/O base address
- * @osc_base: base time of the counter in ns
- * OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
- * @iosize: I/O register size
- * @regshift: register gap shift
- */
-struct comedi_8254 *comedi_8254_init(unsigned long iobase,
- unsigned int osc_base,
- unsigned int iosize,
- unsigned int regshift)
-{
- return __i8254_init(iobase, NULL, osc_base, iosize, regshift);
-}
-EXPORT_SYMBOL_GPL(comedi_8254_init);
-
-/**
- * comedi_8254_mm_init - allocate and initialize the 8254 device for mmio access
- * @mmio: memory mapped I/O base address
- * @osc_base: base time of the counter in ns
- * OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
- * @iosize: I/O register size
- * @regshift: register gap shift
- */
-struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio,
- unsigned int osc_base,
- unsigned int iosize,
- unsigned int regshift)
-{
- return __i8254_init(0, mmio, osc_base, iosize, regshift);
-}
-EXPORT_SYMBOL_GPL(comedi_8254_mm_init);
-
-static int __init comedi_8254_module_init(void)
-{
- return 0;
-}
-module_init(comedi_8254_module_init);
-
-static void __exit comedi_8254_module_exit(void)
-{
-}
-module_exit(comedi_8254_module_exit);
-
-MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
-MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support");
-MODULE_LICENSE("GPL");