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 * @i8254: comedi_8254 struct 559 */ 560 void comedi_8254_subdevice_init(struct comedi_subdevice *s, 561 struct comedi_8254 *i8254) 562 { 563 s->type = COMEDI_SUBD_COUNTER; 564 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 565 s->n_chan = 3; 566 s->maxdata = 0xffff; 567 s->range_table = &range_unknown; 568 s->insn_read = comedi_8254_insn_read; 569 s->insn_write = comedi_8254_insn_write; 570 s->insn_config = comedi_8254_insn_config; 571 572 s->private = i8254; 573 } 574 EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init); 575 576 static struct comedi_8254 *__i8254_init(unsigned long iobase, 577 void __iomem *mmio, 578 unsigned int osc_base, 579 unsigned int iosize, 580 unsigned int regshift) 581 { 582 struct comedi_8254 *i8254; 583 int i; 584 585 /* sanity check that the iosize is valid */ 586 if (!(iosize == I8254_IO8 || iosize == I8254_IO16 || 587 iosize == I8254_IO32)) 588 return NULL; 589 590 i8254 = kzalloc(sizeof(*i8254), GFP_KERNEL); 591 if (!i8254) 592 return NULL; 593 594 i8254->iobase = iobase; 595 i8254->mmio = mmio; 596 i8254->iosize = iosize; 597 i8254->regshift = regshift; 598 599 /* default osc_base to the max speed of a generic 8254 timer */ 600 i8254->osc_base = osc_base ? osc_base : I8254_OSC_BASE_10MHZ; 601 602 /* reset all the counters by setting them to I8254_MODE0 */ 603 for (i = 0; i < 3; i++) 604 comedi_8254_set_mode(i8254, i, I8254_MODE0 | I8254_BINARY); 605 606 return i8254; 607 } 608 609 /** 610 * comedi_8254_init - allocate and initialize the 8254 device for pio access 611 * @iobase: port I/O base address 612 * @osc_base: base time of the counter in ns 613 * OPTIONAL - only used by comedi_8254_cascade_ns_to_timer() 614 * @iosize: I/O register size 615 * @regshift: register gap shift 616 */ 617 struct comedi_8254 *comedi_8254_init(unsigned long iobase, 618 unsigned int osc_base, 619 unsigned int iosize, 620 unsigned int regshift) 621 { 622 return __i8254_init(iobase, NULL, osc_base, iosize, regshift); 623 } 624 EXPORT_SYMBOL_GPL(comedi_8254_init); 625 626 /** 627 * comedi_8254_mm_init - allocate and initialize the 8254 device for mmio access 628 * @mmio: memory mapped I/O base address 629 * @osc_base: base time of the counter in ns 630 * OPTIONAL - only used by comedi_8254_cascade_ns_to_timer() 631 * @iosize: I/O register size 632 * @regshift: register gap shift 633 */ 634 struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio, 635 unsigned int osc_base, 636 unsigned int iosize, 637 unsigned int regshift) 638 { 639 return __i8254_init(0, mmio, osc_base, iosize, regshift); 640 } 641 EXPORT_SYMBOL_GPL(comedi_8254_mm_init); 642 643 static int __init comedi_8254_module_init(void) 644 { 645 return 0; 646 } 647 module_init(comedi_8254_module_init); 648 649 static void __exit comedi_8254_module_exit(void) 650 { 651 } 652 module_exit(comedi_8254_module_exit); 653 654 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 655 MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support"); 656 MODULE_LICENSE("GPL"); 657