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 27 28 29 // Register access. 30 31 Cpm_regs::Cpm_regs(l4_addr_t addr, Clock_base *clocks[], 32 uint32_t exclk_freq) 33 : _clocks(clocks), exclk_freq(exclk_freq) 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_undefined; 93 } 94 95 // Clock sources. 96 97 uint8_t 98 Source::get_source(Cpm_regs ®s) 99 { 100 if (_source.is_defined()) 101 return _source.get_field(regs); 102 else 103 return 0; 104 } 105 106 void 107 Source::set_source(Cpm_regs ®s, uint8_t source) 108 { 109 if (!_source.is_defined()) 110 return; 111 112 _source.set_field(regs, source); 113 } 114 115 // Clock source frequencies. 116 117 uint32_t 118 Source::get_frequency(Cpm_regs ®s) 119 { 120 // Clocks with one source yield that input frequency. 121 122 if (get_number() == 1) 123 return regs.get_clock(get_input(0))->get_frequency(regs); 124 125 // With multiple sources, obtain the selected source for the clock. 126 127 uint8_t source = get_source(regs); 128 enum Clock_identifiers input = get_input(source); 129 130 // Return the frequency of the source. 131 132 if (input != Clock_undefined) 133 return regs.get_clock(input)->get_frequency(regs); 134 else 135 return 0; 136 } 137 138 // Undefined source. 139 140 Source Source::undefined; 141 142 143 144 // Clock control. 145 146 Control_base::~Control_base() 147 { 148 } 149 150 void 151 Control_base::change_disable(Cpm_regs ®s) 152 { 153 (void) regs; 154 } 155 156 void 157 Control_base::change_enable(Cpm_regs ®s) 158 { 159 (void) regs; 160 } 161 162 int 163 Control::have_clock(Cpm_regs ®s) 164 { 165 if (_gate.is_defined()) 166 return !_gate.get_field(regs); 167 else 168 return true; 169 } 170 171 void 172 Control::start_clock(Cpm_regs ®s) 173 { 174 if (_gate.is_defined()) 175 _gate.set_field(regs, 0); 176 } 177 178 void 179 Control::stop_clock(Cpm_regs ®s) 180 { 181 if (_gate.is_defined()) 182 _gate.set_field(regs, 1); 183 } 184 185 void 186 Control::wait_busy(Cpm_regs ®s) 187 { 188 if (_busy.is_defined()) 189 while (_busy.get_field(regs)); 190 } 191 192 void 193 Control::change_disable(Cpm_regs ®s) 194 { 195 if (_change_enable.is_defined()) 196 _change_enable.set_field(regs, 0); 197 } 198 199 void 200 Control::change_enable(Cpm_regs ®s) 201 { 202 if (_change_enable.is_defined()) 203 _change_enable.set_field(regs, 1); 204 } 205 206 // Undefined control. 207 208 Control Control::undefined; 209 210 211 212 // PLL-specific control. 213 214 int 215 Control_pll::have_pll(Cpm_regs ®s) 216 { 217 return _stable.get_field(regs); 218 } 219 220 int 221 Control_pll::pll_enabled(Cpm_regs ®s) 222 { 223 return _enable.get_field(regs); 224 } 225 226 int 227 Control_pll::pll_bypassed(Cpm_regs ®s) 228 { 229 return _bypass.get_field(regs); 230 } 231 232 // Clock control. 233 234 int 235 Control_pll::have_clock(Cpm_regs ®s) 236 { 237 return have_pll(regs) && pll_enabled(regs); 238 } 239 240 void 241 Control_pll::start_clock(Cpm_regs ®s) 242 { 243 _enable.set_field(regs, 1); 244 while (!have_pll(regs)); 245 } 246 247 void 248 Control_pll::stop_clock(Cpm_regs ®s) 249 { 250 _enable.set_field(regs, 0); 251 while (have_pll(regs)); 252 } 253 254 void 255 Control_pll::wait_busy(Cpm_regs ®s) 256 { 257 if (pll_enabled(regs) && !pll_bypassed(regs)) 258 while (!have_pll(regs)); 259 } 260 261 262 263 // Clock dividers. 264 265 Divider_base::~Divider_base() 266 { 267 } 268 269 270 271 uint32_t 272 Divider::get_divider(Cpm_regs ®s) 273 { 274 if (_divider.is_defined()) 275 return _divider.get_field(regs) + 1; 276 else 277 return 1; 278 } 279 280 void 281 Divider::set_divider(Cpm_regs ®s, uint32_t divider) 282 { 283 if (_divider.is_defined()) 284 _divider.set_field(regs, divider - 1); 285 } 286 287 // Output clock frequencies. 288 289 uint32_t 290 Divider::get_frequency(Cpm_regs ®s, uint32_t source_frequency) 291 { 292 return source_frequency / get_divider(regs); 293 } 294 295 int 296 Divider::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 297 { 298 parameters[0] = get_divider(regs); 299 return 1; 300 } 301 302 void 303 Divider::set_parameters(Cpm_regs ®s, uint32_t parameters[]) 304 { 305 set_divider(regs, parameters[0]); 306 } 307 308 // Undefined divider. 309 310 Divider Divider::undefined; 311 312 313 314 // Feedback (13-bit) multiplier. 315 316 uint32_t 317 Divider_pll::get_multiplier(Cpm_regs ®s) 318 { 319 return _multiplier.get_field(regs) + 1; 320 } 321 322 void 323 Divider_pll::set_multiplier(Cpm_regs ®s, uint32_t multiplier) 324 { 325 _multiplier.set_field(regs, multiplier - 1); 326 } 327 328 // Input (6-bit) divider. 329 330 uint32_t 331 Divider_pll::get_input_divider(Cpm_regs ®s) 332 { 333 return _input_divider.get_field(regs) + 1; 334 } 335 336 void 337 Divider_pll::set_input_divider(Cpm_regs ®s, uint32_t divider) 338 { 339 _input_divider.set_field(regs, divider - 1); 340 } 341 342 // Output (dual 3-bit) dividers. 343 344 uint32_t 345 Divider_pll::get_output_divider(Cpm_regs ®s) 346 { 347 uint32_t d0 = _output_divider0.get_field(regs); 348 uint32_t d1 = _output_divider1.get_field(regs); 349 350 return d0 * d1; 351 } 352 353 void 354 Divider_pll::set_output_divider(Cpm_regs ®s, uint32_t divider) 355 { 356 // Assert 1 as a minimum. 357 // Divider 0 must be less than or equal to divider 1. 358 359 uint32_t d0 = (uint32_t) floor(sqrt(divider ? divider : 1)); 360 uint32_t d1 = divider / d0; 361 362 _output_divider0.set_field(regs, d0); 363 _output_divider1.set_field(regs, d1); 364 } 365 366 uint32_t 367 Divider_pll::get_frequency(Cpm_regs ®s, uint32_t source_frequency) 368 { 369 return (source_frequency * get_multiplier(regs)) / 370 (get_input_divider(regs) * get_output_divider(regs)); 371 } 372 373 int 374 Divider_pll::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 375 { 376 parameters[0] = get_multiplier(regs); 377 parameters[1] = get_input_divider(regs); 378 parameters[2] = get_output_divider(regs); 379 return 3; 380 } 381 382 void 383 Divider_pll::set_parameters(Cpm_regs ®s, uint32_t parameters[]) 384 { 385 set_multiplier(regs, parameters[0]); 386 set_input_divider(regs, parameters[1]); 387 set_output_divider(regs, parameters[2]); 388 } 389 390 391 392 // I2S clock divider. 393 394 uint32_t 395 Divider_i2s::get_multiplier(Cpm_regs ®s) 396 { 397 return _multiplier.get_field(regs); 398 } 399 400 uint32_t 401 Divider_i2s::get_divider_N(Cpm_regs ®s) 402 { 403 return _divider_N.get_field(regs); 404 } 405 406 uint32_t 407 Divider_i2s::get_divider_D(Cpm_regs ®s) 408 { 409 return _divider_D.get_field(regs); 410 } 411 412 uint32_t 413 Divider_i2s::get_frequency(Cpm_regs ®s, uint32_t source_frequency) 414 { 415 return (source_frequency * get_multiplier(regs)) / 416 (get_divider_N(regs) * get_divider_D(regs)); 417 } 418 419 int 420 Divider_i2s::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 421 { 422 parameters[0] = get_multiplier(regs); 423 parameters[1] = get_divider_N(regs); 424 parameters[2] = get_divider_D(regs); 425 return 3; 426 } 427 428 void 429 Divider_i2s::set_parameters(Cpm_regs ®s, uint32_t parameters[]) 430 { 431 // Test for N < 2M. 432 433 if (parameters[1] < 2 * parameters[0] ) 434 return; 435 436 _multiplier.set_field(regs, parameters[0]); 437 _divider_N.set_field(regs, parameters[1]); 438 _divider_D.set_field(regs, parameters[2]); 439 } 440 441 442 443 // Clock interface. 444 445 Clock_base::~Clock_base() 446 { 447 } 448 449 450 451 // Null clock. 452 453 int 454 Clock_null::have_clock(Cpm_regs ®s) 455 { 456 (void) regs; 457 return false; 458 } 459 460 void 461 Clock_null::start_clock(Cpm_regs ®s) 462 { 463 (void) regs; 464 } 465 466 void 467 Clock_null::stop_clock(Cpm_regs ®s) 468 { 469 (void) regs; 470 } 471 472 // Output clock frequencies. 473 474 uint32_t 475 Clock_null::get_frequency(Cpm_regs ®s) 476 { 477 (void) regs; 478 return 0; 479 } 480 481 482 483 // Passive clock. 484 485 int 486 Clock_passive::have_clock(Cpm_regs ®s) 487 { 488 (void) regs; 489 return true; 490 } 491 492 void 493 Clock_passive::start_clock(Cpm_regs ®s) 494 { 495 (void) regs; 496 } 497 498 void 499 Clock_passive::stop_clock(Cpm_regs ®s) 500 { 501 (void) regs; 502 } 503 504 // Output clock frequencies. 505 506 uint32_t 507 Clock_passive::get_frequency(Cpm_regs ®s) 508 { 509 // NOTE: Return the external clock frequency. 510 511 return regs.exclk_freq; 512 } 513 514 515 516 // Clock control. 517 518 int 519 Clock_controlled::have_clock(Cpm_regs ®s) 520 { 521 return _get_control().have_clock(regs); 522 } 523 524 void 525 Clock_controlled::start_clock(Cpm_regs ®s) 526 { 527 _get_control().start_clock(regs); 528 } 529 530 void 531 Clock_controlled::stop_clock(Cpm_regs ®s) 532 { 533 _get_control().stop_clock(regs); 534 } 535 536 537 538 // Active clock interface. 539 540 Clock_active::~Clock_active() 541 { 542 } 543 544 // Clock sources. 545 546 uint8_t 547 Clock_active::get_source(Cpm_regs ®s) 548 { 549 return _source.get_source(regs); 550 } 551 552 void 553 Clock_active::set_source(Cpm_regs ®s, uint8_t source) 554 { 555 _get_control().change_enable(regs); 556 _source.set_source(regs, source); 557 _get_control().wait_busy(regs); 558 _get_control().change_disable(regs); 559 } 560 561 // Clock source frequencies. 562 563 uint32_t 564 Clock_active::get_source_frequency(Cpm_regs ®s) 565 { 566 return _source.get_frequency(regs); 567 } 568 569 // Output clock frequencies. 570 571 uint32_t 572 Clock_active::get_frequency(Cpm_regs ®s) 573 { 574 return get_source_frequency(regs); 575 } 576 577 578 579 // Divided clock interface. 580 581 Clock_divided::~Clock_divided() 582 { 583 } 584 585 // Output clock frequencies. 586 587 uint32_t 588 Clock_divided::get_frequency(Cpm_regs ®s) 589 { 590 return _get_divider().get_frequency(regs, get_source_frequency(regs)); 591 } 592 593 int 594 Clock_divided::get_parameters(Cpm_regs ®s, uint32_t parameters[]) 595 { 596 return _get_divider().get_parameters(regs, parameters); 597 } 598 599 void 600 Clock_divided::set_parameters(Cpm_regs ®s, uint32_t parameters[]) 601 { 602 _get_control().change_enable(regs); 603 _get_divider().set_parameters(regs, parameters); 604 _get_control().wait_busy(regs); 605 _get_control().change_disable(regs); 606 } 607 608 609 610 // PLL functionality. 611 612 Pll::~Pll() 613 { 614 } 615 616 uint32_t 617 Pll::get_frequency(Cpm_regs ®s) 618 { 619 if (have_clock(regs)) 620 { 621 if (!_control.pll_bypassed(regs)) 622 return _divider.get_frequency(regs, get_source_frequency(regs)); 623 else 624 return get_source_frequency(regs); 625 } 626 else 627 return 0; 628 }