RTXI 1.3
comedi/comedi/drivers/8253.h
Go to the documentation of this file.
00001 /*
00002     comedi/drivers/8253.h
00003     Header file for 8253
00004 
00005     COMEDI - Linux Control and Measurement Device Interface
00006     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022 */
00023 
00024 #ifndef _8253_H
00025 #define _8253_H
00026 
00027 #ifndef CMDTEST
00028 #include <linux/comedi.h>
00029 #else
00030 #include <comedi.h>
00031 #endif
00032 
00033 #define i8253_cascade_ns_to_timer i8253_cascade_ns_to_timer_2div
00034 
00035 static inline void i8253_cascade_ns_to_timer_2div_old(int i8253_osc_base,
00036         unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
00037         int round_mode)
00038 {
00039         int divider;
00040         int div1, div2;
00041         int div1_glb, div2_glb, ns_glb;
00042         int div1_lub, div2_lub, ns_lub;
00043         int ns;
00044 
00045         divider = (*nanosec + i8253_osc_base / 2) / i8253_osc_base;
00046 
00047         /* find 2 integers 1<={x,y}<=65536 such that x*y is
00048            close to divider */
00049 
00050         div1_lub = div2_lub = 0;
00051         div1_glb = div2_glb = 0;
00052 
00053         ns_glb = 0;
00054         ns_lub = 0xffffffff;
00055 
00056         div2 = 0x10000;
00057         for (div1 = divider / 65536 + 1; div1 < div2; div1++) {
00058                 div2 = divider / div1;
00059 
00060                 ns = i8253_osc_base * div1 * div2;
00061                 if (ns <= *nanosec && ns > ns_glb) {
00062                         ns_glb = ns;
00063                         div1_glb = div1;
00064                         div2_glb = div2;
00065                 }
00066 
00067                 div2++;
00068                 if (div2 <= 65536) {
00069                         ns = i8253_osc_base * div1 * div2;
00070                         if (ns > *nanosec && ns < ns_lub) {
00071                                 ns_lub = ns;
00072                                 div1_lub = div1;
00073                                 div2_lub = div2;
00074                         }
00075                 }
00076         }
00077 
00078         *nanosec = div1_lub * div2_lub * i8253_osc_base;
00079         *d1 = div1_lub & 0xffff;
00080         *d2 = div2_lub & 0xffff;
00081         return;
00082 }
00083 
00084 static inline void i8253_cascade_ns_to_timer_power(int i8253_osc_base,
00085         unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
00086         int round_mode)
00087 {
00088         int div1, div2;
00089         int base;
00090 
00091         for (div1 = 2; div1 <= (1 << 16); div1 <<= 1) {
00092                 base = i8253_osc_base * div1;
00093                 round_mode &= TRIG_ROUND_MASK;
00094                 switch (round_mode) {
00095                 case TRIG_ROUND_NEAREST:
00096                 default:
00097                         div2 = (*nanosec + base / 2) / base;
00098                         break;
00099                 case TRIG_ROUND_DOWN:
00100                         div2 = (*nanosec) / base;
00101                         break;
00102                 case TRIG_ROUND_UP:
00103                         div2 = (*nanosec + base - 1) / base;
00104                         break;
00105                 }
00106                 if (div2 < 2)
00107                         div2 = 2;
00108                 if (div2 <= 65536) {
00109                         *nanosec = div2 * base;
00110                         *d1 = div1 & 0xffff;
00111                         *d2 = div2 & 0xffff;
00112                         return;
00113                 }
00114         }
00115 
00116         /* shouldn't get here */
00117         div1 = 0x10000;
00118         div2 = 0x10000;
00119         *nanosec = div1 * div2 * i8253_osc_base;
00120         *d1 = div1 & 0xffff;
00121         *d2 = div2 & 0xffff;
00122 }
00123 
00124 static inline void i8253_cascade_ns_to_timer_2div(int i8253_osc_base,
00125         unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
00126         int round_mode)
00127 {
00128         unsigned int divider;
00129         unsigned int div1, div2;
00130         unsigned int div1_glb, div2_glb, ns_glb;
00131         unsigned int div1_lub, div2_lub, ns_lub;
00132         unsigned int ns;
00133         unsigned int start;
00134         unsigned int ns_low, ns_high;
00135         static const unsigned int max_count = 0x10000;
00136         /* exit early if everything is already correct (this can save time
00137          * since this function may be called repeatedly during command tests
00138          * and execution) */
00139         div1 = *d1 ? *d1 : max_count;
00140         div2 = *d2 ? *d2 : max_count;
00141         divider = div1 * div2;
00142         if (div1 * div2 * i8253_osc_base == *nanosec &&
00143                 div1 > 1 && div1 <= max_count &&
00144                 div2 > 1 && div2 <= max_count &&
00145                 /* check for overflow */
00146                 divider > div1 && divider > div2 &&
00147                 divider * i8253_osc_base > divider &&
00148                 divider * i8253_osc_base > i8253_osc_base) {
00149                 return;
00150         }
00151 
00152         divider = *nanosec / i8253_osc_base;
00153 
00154         div1_lub = div2_lub = 0;
00155         div1_glb = div2_glb = 0;
00156 
00157         ns_glb = 0;
00158         ns_lub = 0xffffffff;
00159 
00160         div2 = max_count;
00161         start = divider / div2;
00162         if (start < 2)
00163                 start = 2;
00164         for (div1 = start; div1 <= divider / div1 + 1 && div1 <= max_count;
00165                 div1++) {
00166                 for (div2 = divider / div1;
00167                         div1 * div2 <= divider + div1 + 1 && div2 <= max_count;
00168                         div2++) {
00169                         ns = i8253_osc_base * div1 * div2;
00170                         if (ns <= *nanosec && ns > ns_glb) {
00171                                 ns_glb = ns;
00172                                 div1_glb = div1;
00173                                 div2_glb = div2;
00174                         }
00175                         if (ns >= *nanosec && ns < ns_lub) {
00176                                 ns_lub = ns;
00177                                 div1_lub = div1;
00178                                 div2_lub = div2;
00179                         }
00180                 }
00181         }
00182 
00183         round_mode &= TRIG_ROUND_MASK;
00184         switch (round_mode) {
00185         case TRIG_ROUND_NEAREST:
00186         default:
00187                 ns_high = div1_lub * div2_lub * i8253_osc_base;
00188                 ns_low = div1_glb * div2_glb * i8253_osc_base;
00189                 if (ns_high - *nanosec < *nanosec - ns_low) {
00190                         div1 = div1_lub;
00191                         div2 = div2_lub;
00192                 } else {
00193                         div1 = div1_glb;
00194                         div2 = div2_glb;
00195                 }
00196                 break;
00197         case TRIG_ROUND_UP:
00198                 div1 = div1_lub;
00199                 div2 = div2_lub;
00200                 break;
00201         case TRIG_ROUND_DOWN:
00202                 div1 = div1_glb;
00203                 div2 = div2_glb;
00204                 break;
00205         }
00206 
00207         *nanosec = div1 * div2 * i8253_osc_base;
00208         *d1 = div1 & 0xffff;    // masking is done since counter maps zero to 0x10000
00209         *d2 = div2 & 0xffff;
00210         return;
00211 }
00212 
00213 #ifndef CMDTEST
00214 /* i8254_load programs 8254 counter chip.  It should also work for the 8253.
00215  * base_address is the lowest io address for the chip (the address of counter 0).
00216  * counter_number is the counter you want to load (0,1 or 2)
00217  * count is the number to load into the counter.
00218  *
00219  * You probably want to use mode 2.
00220  *
00221  * Use i8254_mm_load() if you board uses memory-mapped io, it is
00222  * the same as i8254_load() except it uses writeb() instead of outb().
00223  *
00224  * Neither i8254_load() or i8254_read() do their loading/reading
00225  * atomically.  The 16 bit read/writes are performed with two successive
00226  * 8 bit read/writes.  So if two parts of your driver do a load/read on
00227  * the same counter, it may be necessary to protect these functions
00228  * with a spinlock.
00229  *
00230  * FMH
00231  */
00232 
00233 #define i8254_control_reg       3
00234 
00235 static inline int i8254_load(unsigned long base_address, unsigned int regshift,
00236         unsigned int counter_number, unsigned int count, unsigned int mode)
00237 {
00238         unsigned int byte;
00239 
00240         if (counter_number > 2)
00241                 return -1;
00242         if (count > 0xffff)
00243                 return -1;
00244         if (mode > 5)
00245                 return -1;
00246         if ((mode == 2 || mode == 3) && count == 1)
00247                 return -1;
00248 
00249         byte = counter_number << 6;
00250         byte |= 0x30;           // load low then high byte
00251         byte |= (mode << 1);    // set counter mode
00252         outb(byte, base_address + (i8254_control_reg << regshift));
00253         byte = count & 0xff;    // lsb of counter value
00254         outb(byte, base_address + (counter_number << regshift));
00255         byte = (count >> 8) & 0xff;     // msb of counter value
00256         outb(byte, base_address + (counter_number << regshift));
00257 
00258         return 0;
00259 }
00260 
00261 static inline int i8254_mm_load(void *base_address, unsigned int regshift,
00262         unsigned int counter_number, unsigned int count, unsigned int mode)
00263 {
00264         unsigned int byte;
00265 
00266         if (counter_number > 2)
00267                 return -1;
00268         if (count > 0xffff)
00269                 return -1;
00270         if (mode > 5)
00271                 return -1;
00272         if ((mode == 2 || mode == 3) && count == 1)
00273                 return -1;
00274 
00275         byte = counter_number << 6;
00276         byte |= 0x30;           // load low then high byte
00277         byte |= (mode << 1);    // set counter mode
00278         writeb(byte, base_address + (i8254_control_reg << regshift));
00279         byte = count & 0xff;    // lsb of counter value
00280         writeb(byte, base_address + (counter_number << regshift));
00281         byte = (count >> 8) & 0xff;     // msb of counter value
00282         writeb(byte, base_address + (counter_number << regshift));
00283 
00284         return 0;
00285 }
00286 
00287 /* Returns 16 bit counter value, should work for 8253 also.*/
00288 static inline int i8254_read(unsigned long base_address, unsigned int regshift,
00289         unsigned int counter_number)
00290 {
00291         unsigned int byte;
00292         int ret;
00293 
00294         if (counter_number > 2)
00295                 return -1;
00296 
00297         // latch counter
00298         byte = counter_number << 6;
00299         outb(byte, base_address + (i8254_control_reg << regshift));
00300 
00301         // read lsb
00302         ret = inb(base_address + (counter_number << regshift));
00303         // read msb
00304         ret += inb(base_address + (counter_number << regshift)) << 8;
00305 
00306         return ret;
00307 }
00308 
00309 static inline int i8254_mm_read(void *base_address, unsigned int regshift,
00310         unsigned int counter_number)
00311 {
00312         unsigned int byte;
00313         int ret;
00314 
00315         if (counter_number > 2)
00316                 return -1;
00317 
00318         // latch counter
00319         byte = counter_number << 6;
00320         writeb(byte, base_address + (i8254_control_reg << regshift));
00321 
00322         // read lsb
00323         ret = readb(base_address + (counter_number << regshift));
00324         // read msb
00325         ret += readb(base_address + (counter_number << regshift)) << 8;
00326 
00327         return ret;
00328 }
00329 
00330 /* Loads 16 bit initial counter value, should work for 8253 also. */
00331 static inline void i8254_write(unsigned long base_address,
00332         unsigned int regshift, unsigned int counter_number, unsigned int count)
00333 {
00334         unsigned int byte;
00335 
00336         if (counter_number > 2)
00337                 return;
00338 
00339         byte = count & 0xff;    // lsb of counter value
00340         outb(byte, base_address + (counter_number << regshift));
00341         byte = (count >> 8) & 0xff;     // msb of counter value
00342         outb(byte, base_address + (counter_number << regshift));
00343 }
00344 
00345 static inline void i8254_mm_write(void *base_address,
00346         unsigned int regshift, unsigned int counter_number, unsigned int count)
00347 {
00348         unsigned int byte;
00349 
00350         if (counter_number > 2)
00351                 return;
00352 
00353         byte = count & 0xff;    // lsb of counter value
00354         writeb(byte, base_address + (counter_number << regshift));
00355         byte = (count >> 8) & 0xff;     // msb of counter value
00356         writeb(byte, base_address + (counter_number << regshift));
00357 }
00358 
00359 /* Set counter mode, should work for 8253 also.
00360  * Note: the 'mode' value is different to that for i8254_load() and comes
00361  * from the INSN_CONFIG_8254_SET_MODE command:
00362  *   I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
00363  * OR'ed with:
00364  *   I8254_BCD, I8254_BINARY
00365  */
00366 static inline int i8254_set_mode(unsigned long base_address,
00367         unsigned int regshift, unsigned int counter_number, unsigned int mode)
00368 {
00369         unsigned int byte;
00370 
00371         if (counter_number > 2)
00372                 return -1;
00373         if (mode > (I8254_MODE5 | I8254_BINARY))
00374                 return -1;
00375 
00376         byte = counter_number << 6;
00377         byte |= 0x30;           // load low then high byte
00378         byte |= mode;           // set counter mode and BCD|binary
00379         outb(byte, base_address + (i8254_control_reg << regshift));
00380 
00381         return 0;
00382 }
00383 
00384 static inline int i8254_mm_set_mode(void *base_address,
00385         unsigned int regshift, unsigned int counter_number, unsigned int mode)
00386 {
00387         unsigned int byte;
00388 
00389         if (counter_number > 2)
00390                 return -1;
00391         if (mode > (I8254_MODE5 | I8254_BINARY))
00392                 return -1;
00393 
00394         byte = counter_number << 6;
00395         byte |= 0x30;           // load low then high byte
00396         byte |= mode;           // set counter mode and BCD|binary
00397         writeb(byte, base_address + (i8254_control_reg << regshift));
00398 
00399         return 0;
00400 }
00401 
00402 static inline int i8254_status(unsigned long base_address,
00403         unsigned int regshift, unsigned int counter_number)
00404 {
00405         outb(0xE0 | (2 << counter_number),
00406                 base_address + (i8254_control_reg << regshift));
00407         return inb(base_address + (counter_number << regshift));
00408 }
00409 
00410 static inline int i8254_mm_status(void *base_address,
00411         unsigned int regshift, unsigned int counter_number)
00412 {
00413         writeb(0xE0 | (2 << counter_number),
00414                 base_address + (i8254_control_reg << regshift));
00415         return readb(base_address + (counter_number << regshift));
00416 }
00417 
00418 #endif
00419 
00420 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines