linux/drivers/staging/comedi/drivers/8253.h
<<
>>
Prefs
   1/*
   2    comedi/drivers/8253.h
   3    Header file for 8253
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   7
   8    This program is free software; you can redistribute it and/or modify
   9    it under the terms of the GNU General Public License as published by
  10    the Free Software Foundation; either version 2 of the License, or
  11    (at your option) any later version.
  12
  13    This program is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16    GNU General Public License for more details.
  17*/
  18
  19#ifndef _8253_H
  20#define _8253_H
  21
  22#include "../comedi.h"
  23
  24#define i8253_cascade_ns_to_timer i8253_cascade_ns_to_timer_2div
  25
  26static inline void i8253_cascade_ns_to_timer_2div_old(int i8253_osc_base,
  27                                                      unsigned int *d1,
  28                                                      unsigned int *d2,
  29                                                      unsigned int *nanosec,
  30                                                      int round_mode)
  31{
  32        int divider;
  33        int div1, div2;
  34        int div1_glb, div2_glb, ns_glb;
  35        int div1_lub, div2_lub, ns_lub;
  36        int ns;
  37
  38        divider = (*nanosec + i8253_osc_base / 2) / i8253_osc_base;
  39
  40        /* find 2 integers 1<={x,y}<=65536 such that x*y is
  41           close to divider */
  42
  43        div1_lub = div2_lub = 0;
  44        div1_glb = div2_glb = 0;
  45
  46        ns_glb = 0;
  47        ns_lub = 0xffffffff;
  48
  49        div2 = 0x10000;
  50        for (div1 = divider / 65536 + 1; div1 < div2; div1++) {
  51                div2 = divider / div1;
  52
  53                ns = i8253_osc_base * div1 * div2;
  54                if (ns <= *nanosec && ns > ns_glb) {
  55                        ns_glb = ns;
  56                        div1_glb = div1;
  57                        div2_glb = div2;
  58                }
  59
  60                div2++;
  61                if (div2 <= 65536) {
  62                        ns = i8253_osc_base * div1 * div2;
  63                        if (ns > *nanosec && ns < ns_lub) {
  64                                ns_lub = ns;
  65                                div1_lub = div1;
  66                                div2_lub = div2;
  67                        }
  68                }
  69        }
  70
  71        *nanosec = div1_lub * div2_lub * i8253_osc_base;
  72        *d1 = div1_lub & 0xffff;
  73        *d2 = div2_lub & 0xffff;
  74        return;
  75}
  76
  77static inline void i8253_cascade_ns_to_timer_power(int i8253_osc_base,
  78                                                   unsigned int *d1,
  79                                                   unsigned int *d2,
  80                                                   unsigned int *nanosec,
  81                                                   int round_mode)
  82{
  83        int div1, div2;
  84        int base;
  85
  86        for (div1 = 2; div1 <= (1 << 16); div1 <<= 1) {
  87                base = i8253_osc_base * div1;
  88                round_mode &= TRIG_ROUND_MASK;
  89                switch (round_mode) {
  90                case TRIG_ROUND_NEAREST:
  91                default:
  92                        div2 = (*nanosec + base / 2) / base;
  93                        break;
  94                case TRIG_ROUND_DOWN:
  95                        div2 = (*nanosec) / base;
  96                        break;
  97                case TRIG_ROUND_UP:
  98                        div2 = (*nanosec + base - 1) / base;
  99                        break;
 100                }
 101                if (div2 < 2)
 102                        div2 = 2;
 103                if (div2 <= 65536) {
 104                        *nanosec = div2 * base;
 105                        *d1 = div1 & 0xffff;
 106                        *d2 = div2 & 0xffff;
 107                        return;
 108                }
 109        }
 110
 111        /* shouldn't get here */
 112        div1 = 0x10000;
 113        div2 = 0x10000;
 114        *nanosec = div1 * div2 * i8253_osc_base;
 115        *d1 = div1 & 0xffff;
 116        *d2 = div2 & 0xffff;
 117}
 118
 119static inline void i8253_cascade_ns_to_timer_2div(int i8253_osc_base,
 120                                                  unsigned int *d1,
 121                                                  unsigned int *d2,
 122                                                  unsigned int *nanosec,
 123                                                  int round_mode)
 124{
 125        unsigned int divider;
 126        unsigned int div1, div2;
 127        unsigned int div1_glb, div2_glb, ns_glb;
 128        unsigned int div1_lub, div2_lub, ns_lub;
 129        unsigned int ns;
 130        unsigned int start;
 131        unsigned int ns_low, ns_high;
 132        static const unsigned int max_count = 0x10000;
 133        /* exit early if everything is already correct (this can save time
 134         * since this function may be called repeatedly during command tests
 135         * and execution) */
 136        div1 = *d1 ? *d1 : max_count;
 137        div2 = *d2 ? *d2 : max_count;
 138        divider = div1 * div2;
 139        if (div1 * div2 * i8253_osc_base == *nanosec &&
 140            div1 > 1 && div1 <= max_count && div2 > 1 && div2 <= max_count &&
 141            /* check for overflow */
 142            divider > div1 && divider > div2 &&
 143            divider * i8253_osc_base > divider &&
 144            divider * i8253_osc_base > i8253_osc_base) {
 145                return;
 146        }
 147
 148        divider = *nanosec / i8253_osc_base;
 149
 150        div1_lub = div2_lub = 0;
 151        div1_glb = div2_glb = 0;
 152
 153        ns_glb = 0;
 154        ns_lub = 0xffffffff;
 155
 156        div2 = max_count;
 157        start = divider / div2;
 158        if (start < 2)
 159                start = 2;
 160        for (div1 = start; div1 <= divider / div1 + 1 && div1 <= max_count;
 161             div1++) {
 162                for (div2 = divider / div1;
 163                     div1 * div2 <= divider + div1 + 1 && div2 <= max_count;
 164                     div2++) {
 165                        ns = i8253_osc_base * div1 * div2;
 166                        if (ns <= *nanosec && ns > ns_glb) {
 167                                ns_glb = ns;
 168                                div1_glb = div1;
 169                                div2_glb = div2;
 170                        }
 171                        if (ns >= *nanosec && ns < ns_lub) {
 172                                ns_lub = ns;
 173                                div1_lub = div1;
 174                                div2_lub = div2;
 175                        }
 176                }
 177        }
 178
 179        round_mode &= TRIG_ROUND_MASK;
 180        switch (round_mode) {
 181        case TRIG_ROUND_NEAREST:
 182        default:
 183                ns_high = div1_lub * div2_lub * i8253_osc_base;
 184                ns_low = div1_glb * div2_glb * i8253_osc_base;
 185                if (ns_high - *nanosec < *nanosec - ns_low) {
 186                        div1 = div1_lub;
 187                        div2 = div2_lub;
 188                } else {
 189                        div1 = div1_glb;
 190                        div2 = div2_glb;
 191                }
 192                break;
 193        case TRIG_ROUND_UP:
 194                div1 = div1_lub;
 195                div2 = div2_lub;
 196                break;
 197        case TRIG_ROUND_DOWN:
 198                div1 = div1_glb;
 199                div2 = div2_glb;
 200                break;
 201        }
 202
 203        *nanosec = div1 * div2 * i8253_osc_base;
 204        /*  masking is done since counter maps zero to 0x10000 */
 205        *d1 = div1 & 0xffff;
 206        *d2 = div2 & 0xffff;
 207        return;
 208}
 209
 210#ifndef CMDTEST
 211/* i8254_load programs 8254 counter chip.  It should also work for the 8253.
 212 * base_address is the lowest io address
 213 * for the chip (the address of counter 0).
 214 * counter_number is the counter you want to load (0,1 or 2)
 215 * count is the number to load into the counter.
 216 *
 217 * You probably want to use mode 2.
 218 *
 219 * Use i8254_mm_load() if you board uses memory-mapped io, it is
 220 * the same as i8254_load() except it uses writeb() instead of outb().
 221 *
 222 * Neither i8254_load() or i8254_read() do their loading/reading
 223 * atomically.  The 16 bit read/writes are performed with two successive
 224 * 8 bit read/writes.  So if two parts of your driver do a load/read on
 225 * the same counter, it may be necessary to protect these functions
 226 * with a spinlock.
 227 *
 228 * FMH
 229 */
 230
 231#define i8254_control_reg       3
 232
 233static inline int i8254_load(unsigned long base_address, unsigned int regshift,
 234                             unsigned int counter_number, unsigned int count,
 235                             unsigned int mode)
 236{
 237        unsigned int byte;
 238
 239        if (counter_number > 2)
 240                return -1;
 241        if (count > 0xffff)
 242                return -1;
 243        if (mode > 5)
 244                return -1;
 245        if ((mode == 2 || mode == 3) && count == 1)
 246                return -1;
 247
 248        byte = counter_number << 6;
 249        byte |= 0x30;           /*  load low then high byte */
 250        byte |= (mode << 1);    /*  set counter mode */
 251        outb(byte, base_address + (i8254_control_reg << regshift));
 252        byte = count & 0xff;    /*  lsb of counter value */
 253        outb(byte, base_address + (counter_number << regshift));
 254        byte = (count >> 8) & 0xff;     /*  msb of counter value */
 255        outb(byte, base_address + (counter_number << regshift));
 256
 257        return 0;
 258}
 259
 260static inline int i8254_mm_load(void __iomem *base_address,
 261                                unsigned int regshift,
 262                                unsigned int counter_number,
 263                                unsigned int count,
 264                                unsigned int mode)
 265{
 266        unsigned int byte;
 267
 268        if (counter_number > 2)
 269                return -1;
 270        if (count > 0xffff)
 271                return -1;
 272        if (mode > 5)
 273                return -1;
 274        if ((mode == 2 || mode == 3) && count == 1)
 275                return -1;
 276
 277        byte = counter_number << 6;
 278        byte |= 0x30;           /*  load low then high byte */
 279        byte |= (mode << 1);    /*  set counter mode */
 280        writeb(byte, base_address + (i8254_control_reg << regshift));
 281        byte = count & 0xff;    /*  lsb of counter value */
 282        writeb(byte, base_address + (counter_number << regshift));
 283        byte = (count >> 8) & 0xff;     /*  msb of counter value */
 284        writeb(byte, base_address + (counter_number << regshift));
 285
 286        return 0;
 287}
 288
 289/* Returns 16 bit counter value, should work for 8253 also.*/
 290static inline int i8254_read(unsigned long base_address, unsigned int regshift,
 291                             unsigned int counter_number)
 292{
 293        unsigned int byte;
 294        int ret;
 295
 296        if (counter_number > 2)
 297                return -1;
 298
 299        /*  latch counter */
 300        byte = counter_number << 6;
 301        outb(byte, base_address + (i8254_control_reg << regshift));
 302
 303        /*  read lsb */
 304        ret = inb(base_address + (counter_number << regshift));
 305        /*  read msb */
 306        ret += inb(base_address + (counter_number << regshift)) << 8;
 307
 308        return ret;
 309}
 310
 311static inline int i8254_mm_read(void __iomem *base_address,
 312                                unsigned int regshift,
 313                                unsigned int counter_number)
 314{
 315        unsigned int byte;
 316        int ret;
 317
 318        if (counter_number > 2)
 319                return -1;
 320
 321        /*  latch counter */
 322        byte = counter_number << 6;
 323        writeb(byte, base_address + (i8254_control_reg << regshift));
 324
 325        /*  read lsb */
 326        ret = readb(base_address + (counter_number << regshift));
 327        /*  read msb */
 328        ret += readb(base_address + (counter_number << regshift)) << 8;
 329
 330        return ret;
 331}
 332
 333/* Loads 16 bit initial counter value, should work for 8253 also. */
 334static inline void i8254_write(unsigned long base_address,
 335                               unsigned int regshift,
 336                               unsigned int counter_number, unsigned int count)
 337{
 338        unsigned int byte;
 339
 340        if (counter_number > 2)
 341                return;
 342
 343        byte = count & 0xff;    /*  lsb of counter value */
 344        outb(byte, base_address + (counter_number << regshift));
 345        byte = (count >> 8) & 0xff;     /*  msb of counter value */
 346        outb(byte, base_address + (counter_number << regshift));
 347}
 348
 349static inline void i8254_mm_write(void __iomem *base_address,
 350                                  unsigned int regshift,
 351                                  unsigned int counter_number,
 352                                  unsigned int count)
 353{
 354        unsigned int byte;
 355
 356        if (counter_number > 2)
 357                return;
 358
 359        byte = count & 0xff;    /*  lsb of counter value */
 360        writeb(byte, base_address + (counter_number << regshift));
 361        byte = (count >> 8) & 0xff;     /*  msb of counter value */
 362        writeb(byte, base_address + (counter_number << regshift));
 363}
 364
 365/* Set counter mode, should work for 8253 also.
 366 * Note: the 'mode' value is different to that for i8254_load() and comes
 367 * from the INSN_CONFIG_8254_SET_MODE command:
 368 *   I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
 369 * OR'ed with:
 370 *   I8254_BCD, I8254_BINARY
 371 */
 372static inline int i8254_set_mode(unsigned long base_address,
 373                                 unsigned int regshift,
 374                                 unsigned int counter_number, unsigned int mode)
 375{
 376        unsigned int byte;
 377
 378        if (counter_number > 2)
 379                return -1;
 380        if (mode > (I8254_MODE5 | I8254_BINARY))
 381                return -1;
 382
 383        byte = counter_number << 6;
 384        byte |= 0x30;           /*  load low then high byte */
 385        byte |= mode;           /*  set counter mode and BCD|binary */
 386        outb(byte, base_address + (i8254_control_reg << regshift));
 387
 388        return 0;
 389}
 390
 391static inline int i8254_mm_set_mode(void __iomem *base_address,
 392                                    unsigned int regshift,
 393                                    unsigned int counter_number,
 394                                    unsigned int mode)
 395{
 396        unsigned int byte;
 397
 398        if (counter_number > 2)
 399                return -1;
 400        if (mode > (I8254_MODE5 | I8254_BINARY))
 401                return -1;
 402
 403        byte = counter_number << 6;
 404        byte |= 0x30;           /*  load low then high byte */
 405        byte |= mode;           /*  set counter mode and BCD|binary */
 406        writeb(byte, base_address + (i8254_control_reg << regshift));
 407
 408        return 0;
 409}
 410
 411static inline int i8254_status(unsigned long base_address,
 412                               unsigned int regshift,
 413                               unsigned int counter_number)
 414{
 415        outb(0xE0 | (2 << counter_number),
 416             base_address + (i8254_control_reg << regshift));
 417        return inb(base_address + (counter_number << regshift));
 418}
 419
 420static inline int i8254_mm_status(void __iomem *base_address,
 421                                  unsigned int regshift,
 422                                  unsigned int counter_number)
 423{
 424        writeb(0xE0 | (2 << counter_number),
 425               base_address + (i8254_control_reg << regshift));
 426        return readb(base_address + (counter_number << regshift));
 427}
 428
 429#endif
 430
 431#endif
 432