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