linux/drivers/staging/comedi/drivers/comedi_8254.c
<<
>>
Prefs
   1/*
   2 * comedi_8254.c
   3 * Generic 8254 timer/counter support
   4 * Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
   5 *
   6 * Based on 8253.h and various subdevice implementations in comedi drivers.
   7 *
   8 * COMEDI - Linux Control and Measurement Device Interface
   9 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License as published by
  13 * the Free Software Foundation; either version 2 of the License, or
  14 * (at your option) any later version.
  15 *
  16 * This program is distributed in the hope that it will be useful,
  17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19 * GNU General Public License for more details.
  20 */
  21
  22/*
  23 * Module: comedi_8254
  24 * Description: Generic 8254 timer/counter support
  25 * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
  26 * Updated: Thu Jan 8 16:45:45 MST 2015
  27 * Status: works
  28 *
  29 * This module is not used directly by end-users. Rather, it is used by other
  30 * drivers to provide support for an 8254 Programmable Interval Timer. These
  31 * counters are typically used to generate the pacer clock used for data
  32 * acquisition. Some drivers also expose the counters for general purpose use.
  33 *
  34 * This module provides the following basic functions:
  35 *
  36 * comedi_8254_init() / comedi_8254_mm_init()
  37 *      Initializes this module to access the 8254 registers. The _mm version
  38 *      sets up the module for MMIO register access the other for PIO access.
  39 *      The pointer returned from these functions is normally stored in the
  40 *      comedi_device dev->pacer and will be freed by the comedi core during
  41 *      the driver (*detach). If a driver has multiple 8254 devices, they need
  42 *      to be stored in the drivers private data and freed when the driver is
  43 *      detached.
  44 *
  45 *      NOTE: The counters are reset by setting them to I8254_MODE0 as part of
  46 *      this initialization.
  47 *
  48 * comedi_8254_set_mode()
  49 *      Sets a counters operation mode:
  50 *              I8254_MODE0     Interrupt on terminal count
  51 *              I8254_MODE1     Hardware retriggerable one-shot
  52 *              I8254_MODE2     Rate generator
  53 *              I8254_MODE3     Square wave mode
  54 *              I8254_MODE4     Software triggered strobe
  55 *              I8254_MODE5     Hardware triggered strobe (retriggerable)
  56 *
  57 *      In addition I8254_BCD and I8254_BINARY specify the counting mode:
  58 *              I8254_BCD       BCD counting
  59 *              I8254_BINARY    Binary counting
  60 *
  61 * comedi_8254_write()
  62 *      Writes an initial value to a counter.
  63 *
  64 *      The largest possible initial count is 0; this is equivalent to 2^16
  65 *      for binary counting and 10^4 for BCD counting.
  66 *
  67 *      NOTE: The counter does not stop when it reaches zero. In Mode 0, 1, 4,
  68 *      and 5 the counter "wraps around" to the highest count, either 0xffff
  69 *      for binary counting or 9999 for BCD counting, and continues counting.
  70 *      Modes 2 and 3 are periodic; the counter reloads itself with the initial
  71 *      count and continues counting from there.
  72 *
  73 * comedi_8254_read()
  74 *      Reads the current value from a counter.
  75 *
  76 * comedi_8254_status()
  77 *      Reads the status of a counter.
  78 *
  79 * comedi_8254_load()
  80 *      Sets a counters operation mode and writes the initial value.
  81 *
  82 * Typically the pacer clock is created by cascading two of the 16-bit counters
  83 * to create a 32-bit rate generator (I8254_MODE2). These functions are
  84 * provided to handle the cascaded counters:
  85 *
  86 * comedi_8254_ns_to_timer()
  87 *      Calculates the divisor value needed for a single counter to generate
  88 *      ns timing.
  89 *
  90 * comedi_8254_cascade_ns_to_timer()
  91 *      Calculates the two divisor values needed to the generate the pacer
  92 *      clock (in ns).
  93 *
  94 * comedi_8254_update_divisors()
  95 *      Transfers the intermediate divisor values to the current divisors.
  96 *
  97 * comedi_8254_pacer_enable()
  98 *      Programs the mode of the cascaded counters and writes the current
  99 *      divisor values.
 100 *
 101 * To expose the counters as a subdevice for general purpose use the following
 102 * functions a provided:
 103 *
 104 * comedi_8254_subdevice_init()
 105 *      Initializes a comedi_subdevice to use the 8254 timer.
 106 *
 107 * comedi_8254_set_busy()
 108 *      Internally flags a counter as "busy". This is done to protect the
 109 *      counters that are used for the cascaded 32-bit pacer.
 110 *
 111 * The subdevice provides (*insn_read) and (*insn_write) operations to read
 112 * the current value and write an initial value to a counter. A (*insn_config)
 113 * operation is also provided to handle the following comedi instructions:
 114 *
 115 *      INSN_CONFIG_SET_COUNTER_MODE    calls comedi_8254_set_mode()
 116 *      INSN_CONFIG_8254_READ_STATUS    calls comedi_8254_status()
 117 *
 118 * The (*insn_config) member of comedi_8254 can be initialized by the external
 119 * driver to handle any additional instructions.
 120 *
 121 * NOTE: Gate control, clock routing, and any interrupt handling for the
 122 * counters is not handled by this module. These features are driver dependent.
 123 */
 124
 125#include <linux/module.h>
 126#include <linux/slab.h>
 127#include <linux/io.h>
 128
 129#include "../comedidev.h"
 130
 131#include "comedi_8254.h"
 132
 133static unsigned int __i8254_read(struct comedi_8254 *i8254, unsigned int reg)
 134{
 135        unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
 136        unsigned int val;
 137
 138        switch (i8254->iosize) {
 139        default:
 140        case I8254_IO8:
 141                if (i8254->mmio)
 142                        val = readb(i8254->mmio + reg_offset);
 143                else
 144                        val = inb(i8254->iobase + reg_offset);
 145                break;
 146        case I8254_IO16:
 147                if (i8254->mmio)
 148                        val = readw(i8254->mmio + reg_offset);
 149                else
 150                        val = inw(i8254->iobase + reg_offset);
 151                break;
 152        case I8254_IO32:
 153                if (i8254->mmio)
 154                        val = readl(i8254->mmio + reg_offset);
 155                else
 156                        val = inl(i8254->iobase + reg_offset);
 157                break;
 158        }
 159        return val & 0xff;
 160}
 161
 162static void __i8254_write(struct comedi_8254 *i8254,
 163                          unsigned int val, unsigned int reg)
 164{
 165        unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
 166
 167        switch (i8254->iosize) {
 168        default:
 169        case I8254_IO8:
 170                if (i8254->mmio)
 171                        writeb(val, i8254->mmio + reg_offset);
 172                else
 173                        outb(val, i8254->iobase + reg_offset);
 174                break;
 175        case I8254_IO16:
 176                if (i8254->mmio)
 177                        writew(val, i8254->mmio + reg_offset);
 178                else
 179                        outw(val, i8254->iobase + reg_offset);
 180                break;
 181        case I8254_IO32:
 182                if (i8254->mmio)
 183                        writel(val, i8254->mmio + reg_offset);
 184                else
 185                        outl(val, i8254->iobase + reg_offset);
 186                break;
 187        }
 188}
 189
 190/**
 191 * comedi_8254_status - return the status of a counter
 192 * @i8254:      comedi_8254 struct for the timer
 193 * @counter:    the counter number
 194 */
 195unsigned int comedi_8254_status(struct comedi_8254 *i8254, unsigned int counter)
 196{
 197        unsigned int cmd;
 198
 199        if (counter > 2)
 200                return 0;
 201
 202        cmd = I8254_CTRL_READBACK_STATUS | I8254_CTRL_READBACK_SEL_CTR(counter);
 203        __i8254_write(i8254, cmd, I8254_CTRL_REG);
 204
 205        return __i8254_read(i8254, counter);
 206}
 207EXPORT_SYMBOL_GPL(comedi_8254_status);
 208
 209/**
 210 * comedi_8254_read - read the current counter value
 211 * @i8254:      comedi_8254 struct for the timer
 212 * @counter:    the counter number
 213 */
 214unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter)
 215{
 216        unsigned int val;
 217
 218        if (counter > 2)
 219                return 0;
 220
 221        /* latch counter */
 222        __i8254_write(i8254, I8254_CTRL_SEL_CTR(counter) | I8254_CTRL_LATCH,
 223                      I8254_CTRL_REG);
 224
 225        /* read LSB then MSB */
 226        val = __i8254_read(i8254, counter);
 227        val |= (__i8254_read(i8254, counter) << 8);
 228
 229        return val;
 230}
 231EXPORT_SYMBOL_GPL(comedi_8254_read);
 232
 233/**
 234 * comedi_8254_write - load a 16-bit initial counter value
 235 * @i8254:      comedi_8254 struct for the timer
 236 * @counter:    the counter number
 237 * @val:        the initial value
 238 */
 239void comedi_8254_write(struct comedi_8254 *i8254,
 240                       unsigned int counter, unsigned int val)
 241{
 242        unsigned int byte;
 243
 244        if (counter > 2)
 245                return;
 246        if (val > 0xffff)
 247                return;
 248
 249        /* load LSB then MSB */
 250        byte = val & 0xff;
 251        __i8254_write(i8254, byte, counter);
 252        byte = (val >> 8) & 0xff;
 253        __i8254_write(i8254, byte, counter);
 254}
 255EXPORT_SYMBOL_GPL(comedi_8254_write);
 256
 257/**
 258 * comedi_8254_set_mode - set the mode of a counter
 259 * @i8254:      comedi_8254 struct for the timer
 260 * @counter:    the counter number
 261 * @mode:       the I8254_MODEx and I8254_BCD|I8254_BINARY
 262 */
 263int comedi_8254_set_mode(struct comedi_8254 *i8254, unsigned int counter,
 264                         unsigned int mode)
 265{
 266        unsigned int byte;
 267
 268        if (counter > 2)
 269                return -EINVAL;
 270        if (mode > (I8254_MODE5 | I8254_BCD))
 271                return -EINVAL;
 272
 273        byte = I8254_CTRL_SEL_CTR(counter) |    /* select counter */
 274               I8254_CTRL_LSB_MSB |             /* load LSB then MSB */
 275               mode;                            /* mode and BCD|binary */
 276        __i8254_write(i8254, byte, I8254_CTRL_REG);
 277
 278        return 0;
 279}
 280EXPORT_SYMBOL_GPL(comedi_8254_set_mode);
 281
 282/**
 283 * comedi_8254_load - program the mode and initial count of a counter
 284 * @i8254:      comedi_8254 struct for the timer
 285 * @counter:    the counter number
 286 * @mode:       the I8254_MODEx and I8254_BCD|I8254_BINARY
 287 * @val:        the initial value
 288 */
 289int comedi_8254_load(struct comedi_8254 *i8254, unsigned int counter,
 290                     unsigned int val, unsigned int mode)
 291{
 292        if (counter > 2)
 293                return -EINVAL;
 294        if (val > 0xffff)
 295                return -EINVAL;
 296        if (mode > (I8254_MODE5 | I8254_BCD))
 297                return -EINVAL;
 298
 299        comedi_8254_set_mode(i8254, counter, mode);
 300        comedi_8254_write(i8254, counter, val);
 301
 302        return 0;
 303}
 304EXPORT_SYMBOL_GPL(comedi_8254_load);
 305
 306/**
 307 * comedi_8254_pacer_enable - set the mode and load the cascaded counters
 308 * @i8254:      comedi_8254 struct for the timer
 309 * @counter1:   the counter number for the first divisor
 310 * @counter2:   the counter number for the second divisor
 311 * @enable:     flag to enable (load) the counters
 312 */
 313void comedi_8254_pacer_enable(struct comedi_8254 *i8254,
 314                              unsigned int counter1,
 315                              unsigned int counter2,
 316                              bool enable)
 317{
 318        unsigned int mode;
 319
 320        if (counter1 > 2 || counter2 > 2 || counter1 == counter2)
 321                return;
 322
 323        if (enable)
 324                mode = I8254_MODE2 | I8254_BINARY;
 325        else
 326                mode = I8254_MODE0 | I8254_BINARY;
 327
 328        comedi_8254_set_mode(i8254, counter1, mode);
 329        comedi_8254_set_mode(i8254, counter2, mode);
 330
 331        if (enable) {
 332                /*
 333                 * Divisors are loaded second counter then first counter to
 334                 * avoid possible issues with the first counter expiring
 335                 * before the second counter is loaded.
 336                 */
 337                comedi_8254_write(i8254, counter2, i8254->divisor2);
 338                comedi_8254_write(i8254, counter1, i8254->divisor1);
 339        }
 340}
 341EXPORT_SYMBOL_GPL(comedi_8254_pacer_enable);
 342
 343/**
 344 * comedi_8254_update_divisors - update the divisors for the cascaded counters
 345 * @i8254:      comedi_8254 struct for the timer
 346 */
 347void comedi_8254_update_divisors(struct comedi_8254 *i8254)
 348{
 349        /* masking is done since counter maps zero to 0x10000 */
 350        i8254->divisor = i8254->next_div & 0xffff;
 351        i8254->divisor1 = i8254->next_div1 & 0xffff;
 352        i8254->divisor2 = i8254->next_div2 & 0xffff;
 353}
 354EXPORT_SYMBOL_GPL(comedi_8254_update_divisors);
 355
 356/**
 357 * comedi_8254_cascade_ns_to_timer - calculate the cascaded divisor values
 358 * @i8254:      comedi_8254 struct for the timer
 359 * @nanosec:    the desired ns time
 360 * @flags:      comedi_cmd flags
 361 */
 362void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254,
 363                                     unsigned int *nanosec,
 364                                     unsigned int flags)
 365{
 366        unsigned int d1 = i8254->next_div1 ? i8254->next_div1 : I8254_MAX_COUNT;
 367        unsigned int d2 = i8254->next_div2 ? i8254->next_div2 : I8254_MAX_COUNT;
 368        unsigned int div = d1 * d2;
 369        unsigned int ns_lub = 0xffffffff;
 370        unsigned int ns_glb = 0;
 371        unsigned int d1_lub = 0;
 372        unsigned int d1_glb = 0;
 373        unsigned int d2_lub = 0;
 374        unsigned int d2_glb = 0;
 375        unsigned int start;
 376        unsigned int ns;
 377        unsigned int ns_low;
 378        unsigned int ns_high;
 379
 380        /* exit early if everything is already correct */
 381        if (div * i8254->osc_base == *nanosec &&
 382            d1 > 1 && d1 <= I8254_MAX_COUNT &&
 383            d2 > 1 && d2 <= I8254_MAX_COUNT &&
 384            /* check for overflow */
 385            div > d1 && div > d2 &&
 386            div * i8254->osc_base > div &&
 387            div * i8254->osc_base > i8254->osc_base)
 388                return;
 389
 390        div = *nanosec / i8254->osc_base;
 391        d2 = I8254_MAX_COUNT;
 392        start = div / d2;
 393        if (start < 2)
 394                start = 2;
 395        for (d1 = start; d1 <= div / d1 + 1 && d1 <= I8254_MAX_COUNT; d1++) {
 396                for (d2 = div / d1;
 397                     d1 * d2 <= div + d1 + 1 && d2 <= I8254_MAX_COUNT; d2++) {
 398                        ns = i8254->osc_base * d1 * d2;
 399                        if (ns <= *nanosec && ns > ns_glb) {
 400                                ns_glb = ns;
 401                                d1_glb = d1;
 402                                d2_glb = d2;
 403                        }
 404                        if (ns >= *nanosec && ns < ns_lub) {
 405                                ns_lub = ns;
 406                                d1_lub = d1;
 407                                d2_lub = d2;
 408                        }
 409                }
 410        }
 411
 412        switch (flags & CMDF_ROUND_MASK) {
 413        case CMDF_ROUND_NEAREST:
 414        default:
 415                ns_high = d1_lub * d2_lub * i8254->osc_base;
 416                ns_low = d1_glb * d2_glb * i8254->osc_base;
 417                if (ns_high - *nanosec < *nanosec - ns_low) {
 418                        d1 = d1_lub;
 419                        d2 = d2_lub;
 420                } else {
 421                        d1 = d1_glb;
 422                        d2 = d2_glb;
 423                }
 424                break;
 425        case CMDF_ROUND_UP:
 426                d1 = d1_lub;
 427                d2 = d2_lub;
 428                break;
 429        case CMDF_ROUND_DOWN:
 430                d1 = d1_glb;
 431                d2 = d2_glb;
 432                break;
 433        }
 434
 435        *nanosec = d1 * d2 * i8254->osc_base;
 436        i8254->next_div1 = d1;
 437        i8254->next_div2 = d2;
 438}
 439EXPORT_SYMBOL_GPL(comedi_8254_cascade_ns_to_timer);
 440
 441/**
 442 * comedi_8254_ns_to_timer - calculate the divisor value for nanosec timing
 443 * @i8254:      comedi_8254 struct for the timer
 444 * @nanosec:    the desired ns time
 445 * @flags:      comedi_cmd flags
 446 */
 447void comedi_8254_ns_to_timer(struct comedi_8254 *i8254,
 448                             unsigned int *nanosec, unsigned int flags)
 449{
 450        unsigned int divisor;
 451
 452        switch (flags & CMDF_ROUND_MASK) {
 453        default:
 454        case CMDF_ROUND_NEAREST:
 455                divisor = DIV_ROUND_CLOSEST(*nanosec, i8254->osc_base);
 456                break;
 457        case CMDF_ROUND_UP:
 458                divisor = DIV_ROUND_UP(*nanosec, i8254->osc_base);
 459                break;
 460        case CMDF_ROUND_DOWN:
 461                divisor = *nanosec / i8254->osc_base;
 462                break;
 463        }
 464        if (divisor < 2)
 465                divisor = 2;
 466        if (divisor > I8254_MAX_COUNT)
 467                divisor = I8254_MAX_COUNT;
 468
 469        *nanosec = divisor * i8254->osc_base;
 470        i8254->next_div = divisor;
 471}
 472EXPORT_SYMBOL_GPL(comedi_8254_ns_to_timer);
 473
 474/**
 475 * comedi_8254_set_busy - set/clear the "busy" flag for a given counter
 476 * @i8254:      comedi_8254 struct for the timer
 477 * @counter:    the counter number
 478 * @busy:       set/clear flag
 479 */
 480void comedi_8254_set_busy(struct comedi_8254 *i8254,
 481                          unsigned int counter, bool busy)
 482{
 483        if (counter < 3)
 484                i8254->busy[counter] = busy;
 485}
 486EXPORT_SYMBOL_GPL(comedi_8254_set_busy);
 487
 488static int comedi_8254_insn_read(struct comedi_device *dev,
 489                                 struct comedi_subdevice *s,
 490                                 struct comedi_insn *insn,
 491                                 unsigned int *data)
 492{
 493        struct comedi_8254 *i8254 = s->private;
 494        unsigned int chan = CR_CHAN(insn->chanspec);
 495        int i;
 496
 497        if (i8254->busy[chan])
 498                return -EBUSY;
 499
 500        for (i = 0; i < insn->n; i++)
 501                data[i] = comedi_8254_read(i8254, chan);
 502
 503        return insn->n;
 504}
 505
 506static int comedi_8254_insn_write(struct comedi_device *dev,
 507                                  struct comedi_subdevice *s,
 508                                  struct comedi_insn *insn,
 509                                  unsigned int *data)
 510{
 511        struct comedi_8254 *i8254 = s->private;
 512        unsigned int chan = CR_CHAN(insn->chanspec);
 513
 514        if (i8254->busy[chan])
 515                return -EBUSY;
 516
 517        if (insn->n)
 518                comedi_8254_write(i8254, chan, data[insn->n - 1]);
 519
 520        return insn->n;
 521}
 522
 523static int comedi_8254_insn_config(struct comedi_device *dev,
 524                                   struct comedi_subdevice *s,
 525                                   struct comedi_insn *insn,
 526                                   unsigned int *data)
 527{
 528        struct comedi_8254 *i8254 = s->private;
 529        unsigned int chan = CR_CHAN(insn->chanspec);
 530        int ret;
 531
 532        if (i8254->busy[chan])
 533                return -EBUSY;
 534
 535        switch (data[0]) {
 536        case INSN_CONFIG_RESET:
 537                ret = comedi_8254_set_mode(i8254, chan,
 538                                           I8254_MODE0 | I8254_BINARY);
 539                if (ret)
 540                        return ret;
 541                break;
 542        case INSN_CONFIG_SET_COUNTER_MODE:
 543                ret = comedi_8254_set_mode(i8254, chan, data[1]);
 544                if (ret)
 545                        return ret;
 546                break;
 547        case INSN_CONFIG_8254_READ_STATUS:
 548                data[1] = comedi_8254_status(i8254, chan);
 549                break;
 550        default:
 551                /*
 552                 * If available, call the driver provided (*insn_config)
 553                 * to handle any driver implemented instructions.
 554                 */
 555                if (i8254->insn_config)
 556                        return i8254->insn_config(dev, s, insn, data);
 557
 558                return -EINVAL;
 559        }
 560
 561        return insn->n;
 562}
 563
 564/**
 565 * comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer
 566 * @s:          comedi_subdevice struct
 567 */
 568void comedi_8254_subdevice_init(struct comedi_subdevice *s,
 569                                struct comedi_8254 *i8254)
 570{
 571        s->type         = COMEDI_SUBD_COUNTER;
 572        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 573        s->n_chan       = 3;
 574        s->maxdata      = 0xffff;
 575        s->range_table  = &range_unknown;
 576        s->insn_read    = comedi_8254_insn_read;
 577        s->insn_write   = comedi_8254_insn_write;
 578        s->insn_config  = comedi_8254_insn_config;
 579
 580        s->private      = i8254;
 581}
 582EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init);
 583
 584static struct comedi_8254 *__i8254_init(unsigned long iobase,
 585                                        void __iomem *mmio,
 586                                        unsigned int osc_base,
 587                                        unsigned int iosize,
 588                                        unsigned int regshift)
 589{
 590        struct comedi_8254 *i8254;
 591        int i;
 592
 593        /* sanity check that the iosize is valid */
 594        if (!(iosize == I8254_IO8 || iosize == I8254_IO16 ||
 595              iosize == I8254_IO32))
 596                return NULL;
 597
 598        i8254 = kzalloc(sizeof(*i8254), GFP_KERNEL);
 599        if (!i8254)
 600                return NULL;
 601
 602        i8254->iobase   = iobase;
 603        i8254->mmio     = mmio;
 604        i8254->iosize   = iosize;
 605        i8254->regshift = regshift;
 606
 607        /* default osc_base to the max speed of a generic 8254 timer */
 608        i8254->osc_base = osc_base ? osc_base : I8254_OSC_BASE_10MHZ;
 609
 610        /* reset all the counters by setting them to I8254_MODE0 */
 611        for (i = 0; i < 3; i++)
 612                comedi_8254_set_mode(i8254, i, I8254_MODE0 | I8254_BINARY);
 613
 614        return i8254;
 615}
 616
 617/**
 618 * comedi_8254_init - allocate and initialize the 8254 device for pio access
 619 * @mmio:       port I/O base address
 620 * @osc_base:   base time of the counter in ns
 621 *              OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
 622 * @iosize:     I/O register size
 623 * @regshift:   register gap shift
 624 */
 625struct comedi_8254 *comedi_8254_init(unsigned long iobase,
 626                                     unsigned int osc_base,
 627                                     unsigned int iosize,
 628                                     unsigned int regshift)
 629{
 630        return __i8254_init(iobase, NULL, osc_base, iosize, regshift);
 631}
 632EXPORT_SYMBOL_GPL(comedi_8254_init);
 633
 634/**
 635 * comedi_8254_mm_init - allocate and initialize the 8254 device for mmio access
 636 * @mmio:       memory mapped I/O base address
 637 * @osc_base:   base time of the counter in ns
 638 *              OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
 639 * @iosize:     I/O register size
 640 * @regshift:   register gap shift
 641 */
 642struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio,
 643                                        unsigned int osc_base,
 644                                        unsigned int iosize,
 645                                        unsigned int regshift)
 646{
 647        return __i8254_init(0, mmio, osc_base, iosize, regshift);
 648}
 649EXPORT_SYMBOL_GPL(comedi_8254_mm_init);
 650
 651static int __init comedi_8254_module_init(void)
 652{
 653        return 0;
 654}
 655module_init(comedi_8254_module_init);
 656
 657static void __exit comedi_8254_module_exit(void)
 658{
 659}
 660module_exit(comedi_8254_module_exit);
 661
 662MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
 663MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support");
 664MODULE_LICENSE("GPL");
 665