root/drivers/staging/comedi/drivers/comedi_8254.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __i8254_read
  2. __i8254_write
  3. comedi_8254_status
  4. comedi_8254_read
  5. comedi_8254_write
  6. comedi_8254_set_mode
  7. comedi_8254_load
  8. comedi_8254_pacer_enable
  9. comedi_8254_update_divisors
  10. comedi_8254_cascade_ns_to_timer
  11. comedi_8254_ns_to_timer
  12. comedi_8254_set_busy
  13. comedi_8254_insn_read
  14. comedi_8254_insn_write
  15. comedi_8254_insn_config
  16. comedi_8254_subdevice_init
  17. __i8254_init
  18. comedi_8254_init
  19. comedi_8254_mm_init
  20. comedi_8254_module_init
  21. comedi_8254_module_exit

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

/* [<][>][^][v][top][bottom][index][help] */