1 /* 2 * Common clock functionality. 3 * 4 * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/devices/hw_mmio_register_block.h> 23 24 #include "cpm-common.h" 25 #include <math.h> 26 #include <stdio.h> 27 28 29 30 // Register access. 31 32 Cpm_regs::Cpm_regs(l4_addr_t addr, Clock_base *clocks[]) 33 : _clocks(clocks) 34 { 35 _regs = new Hw::Mmio_register_block<32>(addr); 36 } 37 38 // Utility methods. 39 40 uint32_t 41 Cpm_regs::get_field(uint32_t reg, uint32_t mask, uint8_t shift) 42 { 43 return (_regs[reg] & (mask << shift)) >> shift; 44 } 45 46 void 47 Cpm_regs::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) 48 { 49 _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); 50 } 51 52 Clock_base * 53 Cpm_regs::get_clock(int num) 54 { 55 return _clocks[num]; 56 } 57 58 59 60 // Field methods. 61 62 uint32_t 63 Field::get_field(Cpm_regs ®s) 64 { 65 if (defined) 66 return regs.get_field(reg, mask, bit); 67 else 68 return 0; 69 } 70 71 void 72 Field::set_field(Cpm_regs ®s, uint32_t value) 73 { 74 if (defined) 75 regs.set_field(reg, mask, bit, value); 76 } 77 78 // Undefined field. 79 80 Field Field::undefined; 81 82 83 84 // Clock sources. 85 86 enum Clock_identifiers 87 Mux::get_input(int num) 88 { 89 if (num < _num_inputs) 90 return _inputs[num]; 91 else 92 return Clock_none; 93 } 94 95 96 97 // Clock sources. 98 99 uint8_t 100 Source::get_source(Cpm_regs ®s) 101 { 102 if (_source.is_defined()) 103 return _source.get_field(regs); 104 else 105 return 0; 106 } 107 108 void 109 Source::set_source(Cpm_regs ®s, uint8_t source) 110 { 111 if (!_source.is_defined()) 112 return; 113 114 _source.set_field(regs, source); 115 } 116 117 enum Clock_identifiers 118 Source::get_source_clock(Cpm_regs ®s) 119 { 120 return get_input(get_number() == 1 ? 0 : get_source(regs)); 121 } 122 123 void 124 Source::set_source_clock(Cpm_regs ®s, enum Clock_identifiers clock) 125 { 126 for (int source = 0; source < _inputs.get_number(); source++) 127 if (get_input(source) == clock) 128 _source.set_field(regs, source); 129 } 130 131 // Clock source frequencies. 132 133 uint64_t 134 Source::get_frequency(Cpm_regs ®s) 135 { 136 enum Clock_identifiers input = get_source_clock(regs); 137 138 if (input != Clock_none) 139 return regs.get_clock(input)->get_frequency(regs); 140 else 141 return 0; 142 } 143 144 145 146 // Clock control. 147 148 Control_base::~Control_base() 149 { 150 } 151 152 void 153 Control_base::change_disable(Cpm_regs ®s) 154 { 155 (void) regs; 156 } 157 158 void 159 Control_base::change_enable(Cpm_regs ®s) 160 { 161 (void) regs; 162 } 163 164 int 165 Control::have_clock(Cpm_regs ®s) 166 { 167 if (_gate.is_defined()) 168 return _gate.get_field(regs) == _gate.get_asserted(); 169 else 170 return true; 171 } 172 173 void 174 Control::start_clock(Cpm_regs ®s) 175 { 176 if (_gate.is_defined()) 177 _gate.set_field(regs, _gate.get_asserted()); 178 } 179 180 void 181 Control::stop_clock(Cpm_regs ®s) 182 { 183 if (_gate.is_defined()) 184 _gate.set_field(regs, _gate.get_deasserted()); 185 } 186 187 void 188 Control::wait_busy(Cpm_regs ®s) 189 { 190 if (_busy.is_defined()) 191 while (_busy.get_field(regs)); 192 } 193 194 void 195 Control::change_disable(Cpm_regs ®s) 196 { 197 if (_change_enable.is_defined()) 198 _change_enable.set_field(regs, 0); 199 } 200 201 void 202 Control::change_enable(Cpm_regs ®s) 203 { 204 if (_change_enable.is_defined()) 205 _change_enable.set_field(regs, 1); 206 } 207 208 // Undefined control. 209 210 Control Control::undefined; 211 212 213 214 // PLL-specific control. 215 216 int 217 Control_pll::have_pll(Cpm_regs ®s) 218 { 219 return _stable.get_field(regs); 220 } 221 222 int 223 Control_pll::pll_enabled(Cpm_regs ®s) 224 { 225 return _enable.get_field(regs); 226 } 227 228 int 229 Control_pll::pll_bypassed(Cpm_regs ®s) 230 { 231 return _bypass.get_field(regs); 232 } 233 234 void 235 Control_pll::pll_bypass(Cpm_regs ®s) 236 { 237 _bypass.set_field(regs, 1); 238 } 239 240 void 241 Control_pll::pll_engage(Cpm_regs ®s) 242 { 243 _bypass.set_field(regs, 0); 244 } 245 246 // Clock control. 247 248 int 249 Control_pll::have_clock(Cpm_regs ®s) 250 { 251 return have_pll(regs) && pll_enabled(regs); 252 } 253 254 void 255 Control_pll::start_clock(Cpm_regs ®s) 256 { 257 _enable.set_field(regs, 1); 258 while (!have_pll(regs)); 259 } 260 261 void 262 Control_pll::stop_clock(Cpm_regs ®s) 263 { 264 _enable.set_field(regs, 0); 265 while (have_pll(regs)); 266 } 267 268 void 269 Control_pll::wait_busy(Cpm_regs ®s) 270 { 271 // NOTE: Could wait for some kind of stable or "lock" signal, but the chips 272 // provide all sorts of differing signals. 273 274 (void) regs; 275 } 276 277 void 278 Control_pll::change_disable(Cpm_regs ®s) 279 { 280 if (_enabled) 281 start_clock(regs); 282 } 283 284 void 285 Control_pll::change_enable(Cpm_regs ®s) 286 { 287 // NOTE: Since the X1600 manual warns of changing the frequency while enabled, 288 // it is easier to just stop and then start the PLL again. 289 290 _enabled = have_clock(regs); 291 292 if (_enabled) 293 stop_clock(regs); 294 } 295 296 297 298 // Clock dividers. 299 300 Divider_base::~Divider_base() 301 { 302 } 303 304 305 306 // Fixed divider. 307 308 uint64_t 309 Divider_fixed::get_frequency(Cpm_regs ®s, uint64_t source_frequency) 310 { 311 (void) regs; 312 return source_frequency / _value; 313 } 314 315 int 316 Divider_fixed::set_frequency(Cpm_regs ®s, uint64_t source_frequency, uint64_t frequency) 317 { 318 (void) regs; (void) source_frequency; (void) frequency; 319 return 0; 320 } 321 322 int 323 Divider_fixed::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 324 { 325 (void) regs; 326 parameters[0] = _value; 327 return 1; 328 } 329 330 int 331 Divider_fixed::set_parameters(Cpm_regs ®s, int num_parameters, uint32_t parameters[]) 332 { 333 (void) regs; (void) num_parameters; (void) parameters; 334 return 0; 335 } 336 337 338 339 // Simple divider for regular clocks. 340 341 uint32_t 342 Divider::get_divider(Cpm_regs ®s) 343 { 344 if (_divider.is_defined()) 345 return _scale * (_divider.get_field(regs) + 1); 346 else 347 return 1; 348 } 349 350 void 351 Divider::set_divider(Cpm_regs ®s, uint32_t divider) 352 { 353 if (_divider.is_defined()) 354 _divider.set_field(regs, divider / _scale - 1); 355 } 356 357 // Output clock frequencies. 358 359 uint64_t 360 Divider::get_frequency(Cpm_regs ®s, uint64_t source_frequency) 361 { 362 return source_frequency / get_divider(regs); 363 } 364 365 int 366 Divider::set_frequency(Cpm_regs ®s, uint64_t source_frequency, uint64_t frequency) 367 { 368 set_divider(regs, (uint32_t) round((double) source_frequency / (double) frequency)); 369 return 1; 370 } 371 372 int 373 Divider::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 374 { 375 parameters[0] = get_divider(regs); 376 return 1; 377 } 378 379 int 380 Divider::set_parameters(Cpm_regs ®s, int num_parameters, uint32_t parameters[]) 381 { 382 if (num_parameters) 383 { 384 set_divider(regs, parameters[0]); 385 return 1; 386 } 387 388 return 0; 389 } 390 391 392 393 // Common divider functionality. 394 395 static int is_integer(double x) 396 { 397 double target = round(x) * 1000; 398 double rounded = floor(x * 1000); 399 400 return (target - 100 < rounded) && (rounded < target + 100); 401 } 402 403 static double getscale_part(double x) 404 { 405 double part = x - floor(x); 406 407 if (part > 0.5) 408 return 1 / (1 - part); 409 else if (part > 0) 410 return 1 / part; 411 else 412 return 1; 413 } 414 415 static double getscale(double x) 416 { 417 double scale = getscale_part(x); 418 419 if (is_integer(scale)) 420 return scale; 421 else 422 return scale * getscale(scale); 423 } 424 425 static void get_divider_operands(double frequency, double source_frequency, 426 double *multiplier, double *divider) 427 { 428 double ratio = frequency / source_frequency; 429 double scale = getscale(ratio); 430 431 *multiplier = scale * ratio; 432 *divider = scale; 433 } 434 435 static void reduce_divider_operands(uint32_t *m, uint32_t *n, uint32_t m_limit, 436 uint32_t n_limit) 437 { 438 while ((*m > m_limit) && (*n > n_limit) && (*m > 1) && (*n > 1)) 439 { 440 *m >>= 1; 441 *n >>= 1; 442 } 443 } 444 445 446 447 #define zero_as_one(X) ((X) ? (X) : 1) 448 449 // Feedback multiplier. 450 451 uint32_t 452 Divider_pll::get_multiplier(Cpm_regs ®s) 453 { 454 if (_adjust_by_one) 455 return _multiplier.get_field(regs) + 1; 456 else 457 return zero_as_one(_multiplier.get_field(regs)); 458 } 459 460 void 461 Divider_pll::set_multiplier(Cpm_regs ®s, uint32_t multiplier) 462 { 463 if (_adjust_by_one) 464 _multiplier.set_field(regs, multiplier - 1); 465 else 466 _multiplier.set_field(regs, multiplier); 467 } 468 469 // Input divider. 470 471 uint32_t 472 Divider_pll::get_input_divider(Cpm_regs ®s) 473 { 474 if (_adjust_by_one) 475 return _input_divider.get_field(regs) + 1; 476 else 477 return zero_as_one(_input_divider.get_field(regs)); 478 } 479 480 void 481 Divider_pll::set_input_divider(Cpm_regs ®s, uint32_t divider) 482 { 483 if (_adjust_by_one) 484 _input_divider.set_field(regs, divider - 1); 485 else 486 _input_divider.set_field(regs, divider); 487 } 488 489 // Output dividers. 490 491 uint32_t 492 Divider_pll::get_output_divider(Cpm_regs ®s) 493 { 494 uint32_t d0, d1; 495 496 if (_adjust_by_one) 497 { 498 d0 = _output_divider0.get_field(regs) + 1; 499 d1 = _output_divider1.is_defined() ? 500 _output_divider1.get_field(regs) + 1 : 1; 501 } 502 else 503 { 504 d0 = zero_as_one(_output_divider0.get_field(regs)); 505 d1 = _output_divider1.is_defined() ? 506 zero_as_one(_output_divider1.get_field(regs)) : 1; 507 } 508 509 return d0 * d1; 510 } 511 512 void 513 Divider_pll::set_output_divider(Cpm_regs ®s, uint32_t divider) 514 { 515 uint32_t d0, d1; 516 517 // Assert 1 as a minimum. 518 519 if (!divider) 520 divider = 1; 521 522 // Attempt to set any single divider. 523 524 if (!_output_divider1.is_defined()) 525 { 526 if (_adjust_by_one) 527 _output_divider0.set_field(regs, divider); 528 else 529 _output_divider0.set_field(regs, divider - 1); 530 return; 531 } 532 533 // For two-divider implementations such as the X1600, divider 0 must be less 534 // than or equal to divider 1. 535 536 if (divider < _output_divider1.get_limit()) 537 { 538 d0 = 1; 539 d1 = divider; 540 } 541 else 542 { 543 d0 = (uint32_t) floor(sqrt(divider)); 544 d1 = divider / d0; 545 } 546 547 if (_adjust_by_one) 548 { 549 _output_divider0.set_field(regs, d0 - 1); 550 _output_divider1.set_field(regs, d1 - 1); 551 } 552 else 553 { 554 _output_divider0.set_field(regs, d0); 555 _output_divider1.set_field(regs, d1); 556 } 557 } 558 559 uint64_t 560 Divider_pll::get_frequency(Cpm_regs ®s, uint64_t source_frequency) 561 { 562 return (source_frequency * get_multiplier(regs)) / 563 (get_input_divider(regs) * get_output_divider(regs)); 564 } 565 566 int 567 Divider_pll::set_frequency(Cpm_regs ®s, uint64_t source_frequency, uint64_t frequency) 568 { 569 double intermediate_multiplier, intermediate_input_divider; 570 uint32_t output_min, output_max, output0, output1; 571 uint32_t multiplier, input_divider, output_divider; 572 573 // Define the range for the output dividers using the intermediate frequency 574 // range applying to each chip, this being the result of the multiplier and 575 // input divider. 576 577 output_min = (uint32_t) ceil(_intermediate_min / frequency); 578 output_max = (uint32_t) floor(_intermediate_max / frequency); 579 580 // Distribute the divider across the input and output dividers. 581 582 output_divider = output_min; 583 584 while (output_divider <= output_max) 585 { 586 bool usable_divider; 587 588 // Test divider constraints. 589 590 if (_output_divider1.is_defined()) 591 { 592 output0 = (uint32_t) floor(sqrt(output_divider)); 593 output1 = (uint32_t) floor(output_divider / output0); 594 595 usable_divider = ((output0 * output1 == output_divider) && 596 (output0 <= _output_divider0.get_limit()) && 597 (output1 <= _output_divider1.get_limit())); 598 } 599 else 600 usable_divider = output_divider <= _output_divider0.get_limit(); 601 602 // Apply any usable divider. 603 604 if (usable_divider) 605 { 606 // Calculate the other parameters. Start by working back from the desired 607 // output frequency to obtain an intermediate frequency using the proposed 608 // divider. 609 610 double intermediate_frequency = frequency * output_divider; 611 612 // Calculate the required multiplier and divider. 613 614 get_divider_operands(intermediate_frequency, source_frequency, 615 &intermediate_multiplier, &intermediate_input_divider); 616 617 multiplier = (uint32_t) round(intermediate_multiplier); 618 input_divider = (uint32_t) round(intermediate_input_divider); 619 620 // Attempt to reduce the multiplier and divider to usable values. 621 622 uint32_t multiplier_limit = _multiplier.get_limit(); 623 uint32_t input_divider_limit = _input_divider.get_limit(); 624 625 reduce_divider_operands(&multiplier, &input_divider, 626 multiplier_limit, input_divider_limit); 627 628 if ((multiplier <= multiplier_limit) && (input_divider <= input_divider_limit)) 629 { 630 set_multiplier(regs, multiplier); 631 set_input_divider(regs, input_divider); 632 set_output_divider(regs, output_divider); 633 634 return 1; 635 } 636 } 637 638 output_divider++; 639 } 640 641 return 0; 642 } 643 644 int 645 Divider_pll::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 646 { 647 parameters[0] = get_multiplier(regs); 648 parameters[1] = get_input_divider(regs); 649 parameters[2] = get_output_divider(regs); 650 return 3; 651 } 652 653 int 654 Divider_pll::set_parameters(Cpm_regs ®s, int num_parameters, uint32_t parameters[]) 655 { 656 if (num_parameters > 2) 657 { 658 set_multiplier(regs, parameters[0]); 659 set_input_divider(regs, parameters[1]); 660 set_output_divider(regs, parameters[2]); 661 662 return 3; 663 } 664 665 return 0; 666 } 667 668 669 670 // I2S clock divider. 671 672 uint32_t 673 Divider_i2s::get_multiplier(Cpm_regs ®s) 674 { 675 return _multiplier.get_field(regs); 676 } 677 678 uint32_t 679 Divider_i2s::get_divider_N(Cpm_regs ®s) 680 { 681 return _divider_N.get_field(regs); 682 } 683 684 uint32_t 685 Divider_i2s::get_divider_D(Cpm_regs ®s) 686 { 687 return _divider_D.get_field(regs); 688 } 689 690 uint64_t 691 Divider_i2s::get_frequency(Cpm_regs ®s, uint64_t source_frequency) 692 { 693 /* NOTE: Assuming that this is the formula, given that the manual does not 694 really describe how D is used. */ 695 696 return (source_frequency * get_multiplier(regs)) / 697 (get_divider_N(regs) * get_divider_D(regs)); 698 } 699 700 int 701 Divider_i2s::set_frequency(Cpm_regs ®s, uint64_t source_frequency, uint64_t frequency) 702 { 703 double m, n; 704 705 get_divider_operands(frequency, source_frequency, &m, &n); 706 707 uint32_t multiplier = (uint32_t) round(m); 708 uint32_t divider = (uint32_t) round(n); 709 710 reduce_divider_operands(&multiplier, ÷r, 711 _multiplier.get_limit(), 712 _divider_N.get_limit()); 713 714 // Test for operand within limits and the N >= 2M constraint. 715 716 if ((multiplier <= _multiplier.get_limit()) && (divider <= _divider_N.get_limit()) && 717 (divider >= 2 * multiplier)) 718 { 719 /* NOTE: Setting D to 1. Even though it seems that D might also be used, 720 it does not seem necessary in practice, and the documentation is 721 unclear about its use. */ 722 723 uint32_t parameters[] = {multiplier, divider, 1}; 724 725 set_parameters(regs, 3, parameters); 726 return 1; 727 } 728 729 return 0; 730 } 731 732 int 733 Divider_i2s::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 734 { 735 parameters[0] = get_multiplier(regs); 736 parameters[1] = get_divider_N(regs); 737 parameters[2] = get_divider_D(regs); 738 return 3; 739 } 740 741 int 742 Divider_i2s::set_parameters(Cpm_regs ®s, int num_parameters, uint32_t parameters[]) 743 { 744 if (num_parameters == 1) 745 { 746 // Set automatic N and D value calculation if only one parameter is given. 747 748 _auto_N.set_field(regs, 0); 749 _auto_D.set_field(regs, 0); 750 _multiplier.set_field(regs, parameters[0]); 751 752 return 1; 753 } 754 else if (num_parameters > 1) 755 { 756 // Require N >= 2M, returning otherwise. 757 758 if (parameters[1] < 2 * parameters[0]) 759 return 0; 760 761 // Set automatic D value calculation if only two parameters are given. 762 763 _auto_N.set_field(regs, 1); 764 _auto_D.set_field(regs, (num_parameters == 2) ? 0 : 1); 765 766 _multiplier.set_field(regs, parameters[0]); 767 _divider_N.set_field(regs, parameters[1]); 768 769 // Set D explicitly if given. 770 771 if (num_parameters > 2) 772 _divider_D.set_field(regs, parameters[2]); 773 774 return num_parameters; 775 } 776 777 return 0; 778 } 779 780 781 782 // Clock interface. 783 784 Clock_base::~Clock_base() 785 { 786 } 787 788 789 790 // Null clock. 791 792 int 793 Clock_null::have_clock(Cpm_regs ®s) 794 { 795 (void) regs; 796 return false; 797 } 798 799 void 800 Clock_null::start_clock(Cpm_regs ®s) 801 { 802 (void) regs; 803 } 804 805 void 806 Clock_null::stop_clock(Cpm_regs ®s) 807 { 808 (void) regs; 809 } 810 811 // Output clock frequencies. 812 813 uint64_t 814 Clock_null::get_frequency(Cpm_regs ®s) 815 { 816 (void) regs; 817 return 0; 818 } 819 820 821 822 // Passive clock. 823 824 int 825 Clock_passive::have_clock(Cpm_regs ®s) 826 { 827 (void) regs; 828 return true; 829 } 830 831 void 832 Clock_passive::start_clock(Cpm_regs ®s) 833 { 834 (void) regs; 835 } 836 837 void 838 Clock_passive::stop_clock(Cpm_regs ®s) 839 { 840 (void) regs; 841 } 842 843 // Output clock frequencies. 844 845 uint64_t 846 Clock_passive::get_frequency(Cpm_regs ®s) 847 { 848 (void) regs; 849 return _frequency; 850 } 851 852 853 854 // Clock control. 855 856 int 857 Clock_controlled::have_clock(Cpm_regs ®s) 858 { 859 return _get_control().have_clock(regs); 860 } 861 862 void 863 Clock_controlled::start_clock(Cpm_regs ®s) 864 { 865 _get_control().start_clock(regs); 866 } 867 868 void 869 Clock_controlled::stop_clock(Cpm_regs ®s) 870 { 871 _get_control().stop_clock(regs); 872 } 873 874 875 876 // Active clock interface. 877 878 Clock_active::~Clock_active() 879 { 880 } 881 882 // Clock sources. 883 884 uint8_t 885 Clock_active::get_source(Cpm_regs ®s) 886 { 887 return _source.get_source(regs); 888 } 889 890 void 891 Clock_active::set_source(Cpm_regs ®s, uint8_t source) 892 { 893 _get_control().change_enable(regs); 894 _source.set_source(regs, source); 895 _get_control().wait_busy(regs); 896 _get_control().change_disable(regs); 897 } 898 899 enum Clock_identifiers 900 Clock_active::get_source_clock(Cpm_regs ®s) 901 { 902 return _source.get_source_clock(regs); 903 } 904 905 void 906 Clock_active::set_source_clock(Cpm_regs ®s, enum Clock_identifiers clock) 907 { 908 _source.set_source_clock(regs, clock); 909 } 910 911 // Clock source frequencies. 912 913 uint64_t 914 Clock_active::get_source_frequency(Cpm_regs ®s) 915 { 916 return _source.get_frequency(regs); 917 } 918 919 // Output clock frequencies. 920 921 uint64_t 922 Clock_active::get_frequency(Cpm_regs ®s) 923 { 924 return get_source_frequency(regs); 925 } 926 927 928 929 // Divided clock interface. 930 931 Clock_divided_base::~Clock_divided_base() 932 { 933 } 934 935 // Output clock frequencies. 936 937 uint64_t 938 Clock_divided_base::get_frequency(Cpm_regs ®s) 939 { 940 return _get_divider().get_frequency(regs, get_source_frequency(regs)); 941 } 942 943 int 944 Clock_divided_base::set_frequency(Cpm_regs ®s, uint64_t frequency) 945 { 946 _get_control().change_enable(regs); 947 int result = _get_divider().set_frequency(regs, get_source_frequency(regs), frequency); 948 _get_control().wait_busy(regs); 949 _get_control().change_disable(regs); 950 951 return result; 952 } 953 954 int 955 Clock_divided_base::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 956 { 957 return _get_divider().get_parameters(regs, parameters); 958 } 959 960 int 961 Clock_divided_base::set_parameters(Cpm_regs ®s, int num_parameters, uint32_t parameters[]) 962 { 963 _get_control().change_enable(regs); 964 int n = _get_divider().set_parameters(regs, num_parameters, parameters); 965 _get_control().wait_busy(regs); 966 _get_control().change_disable(regs); 967 968 return n; 969 } 970 971 972 973 // PLL functionality. 974 975 Pll::~Pll() 976 { 977 } 978 979 uint64_t 980 Pll::get_frequency(Cpm_regs ®s) 981 { 982 if (!_control.pll_bypassed(regs)) 983 return _divider.get_frequency(regs, get_source_frequency(regs)); 984 else 985 return get_source_frequency(regs); 986 } 987 988 int 989 Pll::set_frequency(Cpm_regs ®s, uint64_t frequency) 990 { 991 int result = Clock_divided_base::set_frequency(regs, frequency); 992 _control.pll_engage(regs); 993 994 return result; 995 }