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