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