1 /* 2 * Clock and power management. This exposes the combined functionality 3 * provided by the jz4780 and related SoCs. The power management 4 * functionality could be exposed using a separate driver. 5 * 6 * Copyright (C) 2017, 2018, 2020, 2021 Paul Boddie <paul@boddie.org.uk> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 of 11 * the License, or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA 22 */ 23 24 #include <l4/devices/hw_mmio_register_block.h> 25 #include "cpm-jz4780.h" 26 27 28 29 enum Regs : unsigned 30 { 31 Clock_control = 0x000, // CPCCR 32 Low_power_control = 0x004, // LCR 33 Pll_control = 0x00c, // CPPCR 34 Pll_control_A = 0x010, // CPAPCR 35 Pll_control_M = 0x014, // CPMPCR 36 Pll_control_E = 0x018, // CPEPCR 37 Pll_control_V = 0x01c, // CPVPCR 38 Clock_gate0 = 0x020, // CLKGR0 39 Clock_gate1 = 0x028, // CLKGR1 40 Sleep_control = 0x024, // OPCR (oscillator and power control) 41 Ddr_divider = 0x02c, // DDRCDR 42 I2s_divider0 = 0x060, // I2SCDR 43 I2s_divider1 = 0x0a0, // I2S1CDR 44 Lcd_divider0 = 0x054, // LP0CDR 45 Lcd_divider1 = 0x064, // LP1CDR 46 Msc_divider0 = 0x068, // MSC0CDR 47 Msc_divider1 = 0x0a4, // MSC1CDR 48 Msc_divider2 = 0x0a8, // MSC2CDR 49 Uhc_divider = 0x06c, // UHCCDR 50 Ssi_divider = 0x074, // SSICDR 51 52 // ... 53 54 Hdmi_divider = 0x08c, // HDMICDR 55 56 // ... 57 }; 58 59 enum Clock_bits : unsigned 60 { 61 Clock_enable = 22, // CE_CPU 62 Clock_pclock_divider = 16, // PDIV (slow APB peripherals) 63 Clock_hclock2_divider = 12, // H2DIV (fast AHB peripherals) 64 Clock_hclock0_divider = 8, // H0DIV (fast AHB peripherals) 65 Clock_cpu_divider = 0, // CDIV 66 }; 67 68 enum Pll_bits : unsigned 69 { 70 Pll_multiplier = 19, // xPLLM 71 Pll_input_division = 13, // xPLLN 72 Pll_output_division = 9, // xPLLOD 73 Pll_stable = 4, // xPLL_ON 74 Pll_bypassed = 1, // xPLLBP 75 Pll_enabled = 0, // xPLLEN 76 }; 77 78 enum Clock_source_bits : unsigned 79 { 80 Clock_source_main = 30, // SEL_SRC (output to SCLK_A) 81 Clock_source_cpu = 28, // SEL_CPLL (output to CCLK) 82 Clock_source_hclock0 = 26, // SEL_H0PLL (output to AHB0) 83 Clock_source_hclock2 = 24, // SEL_H2PLL (output to AHB2) 84 Clock_source_ddr = 30, // DCS 85 Clock_source_i2s = 31, // I2CS 86 Clock_source_lcd = 30, // LPCS 87 Clock_source_hdmi = 30, // HPCS 88 }; 89 90 enum Clock_sources : unsigned 91 { 92 // Main clock sources. 93 94 Source_pll_A = 1, // APLL 95 Source_external = 2, // EXCLK 96 Source_realtime = 3, // RTCLK 97 98 // Stoppable clock sources. 99 100 Source_mux_stopped = 0, 101 Source_mux_main = 1, // SCLK_A 102 Source_mux_pll_M = 2, // MPLL 103 Source_mux_pll_E = 3, // EPLL 104 Source_mux_realtime = 3, // RTCLK (TCK) 105 106 // Unstoppable clock sources. 107 108 Source_main = 0, // SCLK_A 109 Source_pll_M = 1, // MPLL 110 Source_pll_E = 2, // EPLL 111 Source_pll_V = 2, // VPLL 112 Source_otg_phy = 3, // OTG_PHY 113 }; 114 115 enum Clock_gate_bits : unsigned 116 { 117 Clock_gate_lcd1 = 28, // LCD (in CLKGR0) 118 Clock_gate_lcd0 = 27, // TVE (in CLKGR0) 119 Clock_gate_hdmi = 9, // HDMI (in CLKGR1) 120 Clock_gate_smb4 = 12, // SMB4 (in CLKGR1) 121 Clock_gate_smb3 = 0, // SMB3 (in CLKGR1) 122 Clock_gate_smb2 = 25, // SMB2 (in CLKGR0) 123 Clock_gate_smb1 = 6, // SMB1 (in CLKGR0) 124 Clock_gate_smb0 = 5, // SMB0 (in CLKGR0) 125 }; 126 127 enum Divider_bits : unsigned 128 { 129 Ddr_divider_value = 0, // DDRCDR 130 Hdmi_divider_value = 0, // HDMICDR 131 Lcd_divider_value = 0, // LPCDR 132 }; 133 134 enum Lcd_clock_values : unsigned 135 { 136 Lcd_change_enable = 0x10000000, // CE_LCD 137 Lcd_change_busy = 0x08000000, // LCD_BUSY 138 Lcd_clock_stop = 0x04000000, // LCD_STOP 139 }; 140 141 enum Hdmi_divider_values : unsigned 142 { 143 Hdmi_select_mask = 0xc0000000, // HPCS 144 Hdmi_change_enable = 0x20000000, // CE_HDMI 145 Hdmi_change_busy = 0x10000000, // HDMI_BUSY 146 Hdmi_clock_stop = 0x08000000, // HDMI_STOP 147 }; 148 149 150 151 // If implemented as a Hw::Device, various properties would be 152 // initialised in the constructor and obtained from the device tree 153 // definitions. 154 155 Cpm_jz4780_chip::Cpm_jz4780_chip(l4_addr_t addr, uint32_t exclk_freq, uint32_t rtclk_freq) 156 : _exclk_freq(exclk_freq), _rtclk_freq(rtclk_freq) 157 { 158 _regs = new Hw::Mmio_register_block<32>(addr); 159 160 // add_cid("cpm"); 161 // add_cid("cpm-jz4780"); 162 // register_property("exclk_freq", &_exclk_freq); 163 } 164 165 // Clock/timer control. 166 167 uint32_t 168 Cpm_jz4780_chip::get_clock_gate_register(enum Clock_identifiers clock) 169 { 170 switch (clock) 171 { 172 case Clock_hdmi: return Clock_gate1; 173 case Clock_smb4: return Clock_gate1; 174 case Clock_smb3: return Clock_gate1; 175 default: return Clock_gate0; 176 } 177 } 178 179 uint32_t 180 Cpm_jz4780_chip::get_clock_gate_value(enum Clock_identifiers clock) 181 { 182 switch (clock) 183 { 184 case Clock_lcd: return (1 << Clock_gate_lcd1) | (1 << Clock_gate_lcd0); 185 case Clock_hdmi: return (1 << Clock_gate_hdmi); 186 case Clock_smb4: return (1 << Clock_gate_smb4); 187 case Clock_smb3: return (1 << Clock_gate_smb3); 188 case Clock_smb2: return (1 << Clock_gate_smb2); 189 case Clock_smb1: return (1 << Clock_gate_smb1); 190 case Clock_smb0: return (1 << Clock_gate_smb0); 191 default: return 0; 192 } 193 } 194 195 int 196 Cpm_jz4780_chip::have_clock(enum Clock_identifiers clock) 197 { 198 return !(_regs[get_clock_gate_register(clock)] & get_clock_gate_value(clock)); 199 } 200 201 void 202 Cpm_jz4780_chip::start_clock(enum Clock_identifiers clock) 203 { 204 uint32_t gate = get_clock_gate_register(clock); 205 206 _regs[gate] = _regs[gate] & ~get_clock_gate_value(clock); 207 } 208 209 void 210 Cpm_jz4780_chip::stop_clock(enum Clock_identifiers clock) 211 { 212 uint32_t gate = get_clock_gate_register(clock); 213 214 _regs[gate] = _regs[gate] | get_clock_gate_value(clock); 215 } 216 217 218 219 // Utility methods. 220 221 uint32_t 222 Cpm_jz4780_chip::get_field(uint32_t reg, uint32_t mask, uint8_t shift) 223 { 224 return (_regs[reg] & (mask << shift)) >> shift; 225 } 226 227 void 228 Cpm_jz4780_chip::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) 229 { 230 _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); 231 } 232 233 // General clock divider access. 234 235 uint8_t 236 Cpm_jz4780_chip::_get_divider(uint32_t reg, uint32_t mask, uint8_t shift) 237 { 238 uint8_t d = get_field(reg, mask, shift); 239 240 // NOTE: Value 15 stops the clock, 14 presumably resumes the clock. 241 242 return (d < 14) ? d + 1 : 1; 243 } 244 245 246 247 // PLL control. 248 249 // Return whether the PLL is stable. 250 251 int 252 Cpm_jz4780_chip::have_pll(uint32_t pll_reg) 253 { 254 return _regs[pll_reg] & (1 << Pll_stable); 255 } 256 257 int 258 Cpm_jz4780_chip::pll_enabled(uint32_t pll_reg) 259 { 260 return _regs[pll_reg] & (1 << Pll_enabled); 261 } 262 263 int 264 Cpm_jz4780_chip::pll_bypassed(uint32_t pll_reg) 265 { 266 return _regs[pll_reg] & (1 << Pll_bypassed); 267 } 268 269 void 270 Cpm_jz4780_chip::pll_enable(uint32_t pll_reg) 271 { 272 _regs[pll_reg] = _regs[pll_reg] | (1 << Pll_enabled); 273 while (!(_regs[pll_reg] & (1 << Pll_stable))); 274 } 275 276 void 277 Cpm_jz4780_chip::pll_disable(uint32_t pll_reg) 278 { 279 _regs[pll_reg] = _regs[pll_reg] & ~(1 << Pll_enabled); 280 while (_regs[pll_reg] & (1 << Pll_stable)); 281 } 282 283 // Feedback (13-bit) multiplier. 284 285 uint16_t 286 Cpm_jz4780_chip::get_multiplier(uint32_t pll_reg) 287 { 288 return get_field(pll_reg, 0x1fff, Pll_multiplier) + 1; 289 } 290 291 void 292 Cpm_jz4780_chip::set_multiplier(uint32_t pll_reg, uint16_t multiplier) 293 { 294 set_field(pll_reg, 0x1fff, Pll_multiplier, multiplier - 1); 295 } 296 297 // Input (6-bit) divider. 298 299 uint8_t 300 Cpm_jz4780_chip::get_input_division(uint32_t pll_reg) 301 { 302 return get_field(pll_reg, 0x3f, Pll_input_division) + 1; 303 } 304 305 void 306 Cpm_jz4780_chip::set_input_division(uint32_t pll_reg, uint8_t divider) 307 { 308 set_field(pll_reg, 0x3f, Pll_input_division, divider - 1); 309 } 310 311 // Output divider. 312 313 uint8_t 314 Cpm_jz4780_chip::get_output_division(uint32_t pll_reg) 315 { 316 uint8_t d = get_field(pll_reg, 0x0f, Pll_output_division); 317 318 // Zero yields a division of one. Otherwise enforce even results. 319 320 return d == 0 ? 1 : (d + 1) & 0x0e; 321 } 322 323 void 324 Cpm_jz4780_chip::set_output_division(uint32_t pll_reg, uint8_t divider) 325 { 326 uint8_t d = divider <= 1 ? 0 : (divider & 0x0e) - 1; 327 328 set_field(pll_reg, 0x0f, Pll_output_division, d); 329 } 330 331 uint32_t 332 Cpm_jz4780_chip::get_pll_frequency(uint32_t pll_reg) 333 { 334 // Test for PLL enable and not PLL bypass. 335 336 if (pll_enabled(pll_reg) && !pll_bypassed(pll_reg)) 337 return (_exclk_freq * get_multiplier(pll_reg)) / 338 (get_input_division(pll_reg) * get_output_division(pll_reg)); 339 else 340 return _exclk_freq; 341 } 342 343 void 344 Cpm_jz4780_chip::set_pll_parameters(uint32_t pll_reg, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) 345 { 346 set_multiplier(pll_reg, multiplier); 347 set_input_division(pll_reg, in_divider); 348 set_output_division(pll_reg, out_divider); 349 350 if (pll_enabled(pll_reg) && !pll_bypassed(pll_reg)) 351 while (!have_pll(pll_reg)); 352 } 353 354 355 356 // CPU clock (CCLK) divider. 357 358 uint8_t 359 Cpm_jz4780_chip::get_cpu_divider() 360 { 361 return _get_divider(Clock_control, 0xf, Clock_cpu_divider); 362 } 363 364 // Fast peripheral clock (H0CLK) divider. 365 366 uint8_t 367 Cpm_jz4780_chip::get_hclock0_divider() 368 { 369 return _get_divider(Clock_control, 0xf, Clock_hclock0_divider); 370 } 371 372 // Fast peripheral clock (H2CLK) divider. 373 374 uint8_t 375 Cpm_jz4780_chip::get_hclock2_divider() 376 { 377 return _get_divider(Clock_control, 0xf, Clock_hclock2_divider); 378 } 379 380 // Slow peripheral clock (PCLK) divider. 381 382 uint8_t 383 Cpm_jz4780_chip::get_pclock_divider() 384 { 385 return _get_divider(Clock_control, 0xf, Clock_pclock_divider); 386 } 387 388 // HDMI clock divider. 389 390 uint8_t 391 Cpm_jz4780_chip::get_hdmi_divider() 392 { 393 return get_field(Hdmi_divider, 0xff, Hdmi_divider_value) + 1; 394 } 395 396 // LCD clock (LPCLK) divider for LCD0 or LCD1 pixel clock. 397 398 uint8_t 399 Cpm_jz4780_chip::get_lcd_pixel_divider(uint8_t controller) 400 { 401 return get_field(controller ? Lcd_divider1 : Lcd_divider0, 0xff, Lcd_divider_value) + 1; 402 } 403 404 // Memory clock (DDR_CLK) divider. 405 406 uint8_t 407 Cpm_jz4780_chip::get_memory_divider() 408 { 409 return _get_divider(Ddr_divider, 0xf, Ddr_divider_value); 410 } 411 412 // HDMI clock divider. 413 414 void 415 Cpm_jz4780_chip::set_hdmi_divider(uint16_t division) 416 { 417 if ((division < 1) || (division > 256)) 418 return; 419 420 // Enable change. 421 422 _regs[Hdmi_divider] = _regs[Hdmi_divider] | Hdmi_change_enable; 423 424 // Set the divider. 425 426 set_field(Hdmi_divider, 0xff, Hdmi_divider_value, division - 1); 427 428 // Restart clock and disable change. 429 430 while (_regs[Hdmi_divider] & Hdmi_change_busy); 431 _regs[Hdmi_divider] = _regs[Hdmi_divider] & ~Hdmi_change_enable; 432 } 433 434 // LCD pixel clock divider. 435 // NOTE: This only supports the first LCD peripheral. 436 437 void 438 Cpm_jz4780_chip::set_lcd_pixel_divider(uint8_t controller, uint16_t division) 439 { 440 uint32_t divider = controller ? Lcd_divider1 : Lcd_divider0; 441 442 if ((division < 1) || (division > 256)) 443 return; 444 445 // Enable change. 446 447 _regs[divider] = _regs[divider] | Lcd_change_enable; 448 449 // Set the divider. 450 451 set_field(divider, 0xff, Lcd_divider_value, division - 1); 452 453 // Restart clock and disable change. 454 455 while (_regs[divider] & Lcd_change_busy); 456 _regs[divider] = _regs[divider] & ~Lcd_change_enable; 457 } 458 459 460 461 // Clock sources. 462 463 uint8_t 464 Cpm_jz4780_chip::get_memory_source() 465 { 466 return get_field(Ddr_divider, 0x3, Clock_source_ddr); 467 } 468 469 uint32_t 470 Cpm_jz4780_chip::get_memory_source_frequency() 471 { 472 switch (get_memory_source()) 473 { 474 case Source_mux_main: 475 return get_main_frequency(); 476 case Source_mux_pll_M: 477 return get_pll_frequency(Pll_control_M); 478 default: 479 return 0; 480 } 481 } 482 483 uint8_t 484 Cpm_jz4780_chip::get_cpu_source() 485 { 486 return get_field(Clock_control, 0x3, Clock_source_cpu); 487 } 488 489 uint32_t 490 Cpm_jz4780_chip::get_cpu_source_frequency() 491 { 492 switch (get_cpu_source()) 493 { 494 case Source_mux_main: 495 return get_main_frequency(); 496 case Source_mux_pll_M: 497 return get_pll_frequency(Pll_control_M); 498 case Source_mux_pll_E: 499 return get_pll_frequency(Pll_control_E); 500 default: 501 return 0; 502 } 503 } 504 505 uint8_t 506 Cpm_jz4780_chip::get_hclock0_source() 507 { 508 return get_field(Clock_control, 0x3, Clock_source_hclock0); 509 } 510 511 uint32_t 512 Cpm_jz4780_chip::get_hclock0_source_frequency() 513 { 514 switch (get_hclock0_source()) 515 { 516 case Source_mux_main: 517 return get_main_frequency(); 518 case Source_mux_pll_M: 519 return get_pll_frequency(Pll_control_M); 520 case Source_mux_pll_E: 521 return get_pll_frequency(Pll_control_E); 522 default: 523 return 0; 524 } 525 } 526 527 uint8_t 528 Cpm_jz4780_chip::get_hclock2_source() 529 { 530 return get_field(Clock_control, 0x3, Clock_source_hclock2); 531 } 532 533 uint32_t 534 Cpm_jz4780_chip::get_hclock2_source_frequency() 535 { 536 switch (get_hclock2_source()) 537 { 538 case Source_mux_main: 539 return get_main_frequency(); 540 case Source_mux_pll_M: 541 return get_pll_frequency(Pll_control_M); 542 case Source_mux_realtime: 543 return _rtclk_freq; // "TCK" in the manual, RTCLK in Linux driver code 544 default: 545 return 0; 546 } 547 } 548 549 void 550 Cpm_jz4780_chip::set_hclock2_source(uint8_t source) 551 { 552 set_field(Clock_control, 0x3, Clock_source_hclock2, source); 553 } 554 555 uint8_t 556 Cpm_jz4780_chip::get_hdmi_source() 557 { 558 return get_field(Hdmi_divider, 0x3, Clock_source_hdmi); 559 } 560 561 uint32_t 562 Cpm_jz4780_chip::get_hdmi_source_frequency() 563 { 564 switch (get_hdmi_source()) 565 { 566 case Source_main: 567 return get_main_frequency(); 568 case Source_pll_M: 569 return get_pll_frequency(Pll_control_M); 570 case Source_pll_V: 571 return get_pll_frequency(Pll_control_V); 572 default: 573 return 0; 574 } 575 } 576 577 void 578 Cpm_jz4780_chip::set_hdmi_source(uint8_t source) 579 { 580 // Stop clock and enable change. 581 582 _regs[Hdmi_divider] = _regs[Hdmi_divider] | Hdmi_change_enable | Hdmi_clock_stop; 583 584 // Set the source. 585 586 set_field(Hdmi_divider, 0x03, Clock_source_hdmi, source); 587 588 // Restart clock and disable change. 589 590 while (_regs[Hdmi_divider] & Hdmi_change_busy); 591 _regs[Hdmi_divider] = _regs[Hdmi_divider] & ~(Hdmi_change_enable | Hdmi_clock_stop); 592 } 593 594 uint8_t 595 Cpm_jz4780_chip::get_lcd_source(uint8_t controller) 596 { 597 return get_field(controller ? Lcd_divider1 : Lcd_divider0, 0x3, Clock_source_lcd); 598 } 599 600 uint32_t 601 Cpm_jz4780_chip::get_lcd_source_frequency(uint8_t controller) 602 { 603 switch (get_lcd_source(controller)) 604 { 605 case Source_main: 606 return get_main_frequency(); 607 case Source_pll_M: 608 return get_pll_frequency(Pll_control_M); 609 case Source_pll_V: 610 return get_pll_frequency(Pll_control_V); 611 default: 612 return 0; 613 } 614 } 615 616 void 617 Cpm_jz4780_chip::set_lcd_source(uint8_t controller, uint8_t source) 618 { 619 uint32_t divider = controller ? Lcd_divider1 : Lcd_divider0; 620 621 // Stop clock and enable change. 622 623 _regs[divider] = _regs[divider] | Lcd_change_enable | Lcd_clock_stop; 624 625 // Set the source. 626 627 set_field(divider, 0x03, Clock_source_lcd, source); 628 629 // Restart clock and disable change. 630 631 while (_regs[divider] & Lcd_change_busy); 632 _regs[divider] = _regs[divider] & ~(Lcd_change_enable | Lcd_clock_stop); 633 } 634 635 uint8_t 636 Cpm_jz4780_chip::get_pclock_source() 637 { 638 return get_hclock2_source(); 639 } 640 641 uint32_t 642 Cpm_jz4780_chip::get_pclock_source_frequency() 643 { 644 return get_hclock2_source_frequency(); 645 } 646 647 void 648 Cpm_jz4780_chip::set_pclock_source(uint8_t source) 649 { 650 set_hclock2_source(source); 651 } 652 653 654 655 // Source frequency, used by various clock sources. 656 657 uint8_t 658 Cpm_jz4780_chip::get_main_source() 659 { 660 return get_field(Clock_control, 0x3, Clock_source_main); 661 } 662 663 uint32_t 664 Cpm_jz4780_chip::get_main_frequency() 665 { 666 switch (get_main_source()) 667 { 668 case Source_pll_A: 669 return get_pll_frequency(Pll_control_A); 670 case Source_external: 671 return _exclk_freq; 672 case Source_realtime: 673 return _rtclk_freq; 674 default: 675 return 0; 676 } 677 } 678 679 // Clock frequency for the CPU. 680 681 uint32_t 682 Cpm_jz4780_chip::get_cpu_frequency() 683 { 684 return get_cpu_source_frequency() / get_cpu_divider(); 685 } 686 687 // Clock frequency for fast peripherals. 688 689 uint32_t 690 Cpm_jz4780_chip::get_hclock0_frequency() 691 { 692 return get_hclock0_source_frequency() / get_hclock0_divider(); 693 } 694 695 // Clock frequency for fast peripherals. 696 697 uint32_t 698 Cpm_jz4780_chip::get_hclock2_frequency() 699 { 700 return get_hclock2_source_frequency() / get_hclock2_divider(); 701 } 702 703 // Clock frequency for slow peripherals. 704 705 uint32_t 706 Cpm_jz4780_chip::get_pclock_frequency() 707 { 708 return get_pclock_source_frequency() / get_pclock_divider(); 709 } 710 711 // Clock frequency for the memory. 712 713 uint32_t 714 Cpm_jz4780_chip::get_memory_frequency() 715 { 716 return get_memory_source_frequency() / get_memory_divider(); 717 } 718 719 uint32_t 720 Cpm_jz4780_chip::get_apll_frequency() 721 { 722 return get_pll_frequency(Pll_control_A); 723 } 724 725 uint32_t 726 Cpm_jz4780_chip::get_epll_frequency() 727 { 728 return get_pll_frequency(Pll_control_E); 729 } 730 731 uint32_t 732 Cpm_jz4780_chip::get_mpll_frequency() 733 { 734 return get_pll_frequency(Pll_control_M); 735 } 736 737 uint32_t 738 Cpm_jz4780_chip::get_vpll_frequency() 739 { 740 return get_pll_frequency(Pll_control_V); 741 } 742 743 744 745 uint32_t 746 Cpm_jz4780_chip::get_frequency(enum Clock_frequency_identifiers clock) 747 { 748 switch (clock) 749 { 750 // NOTE: Returning only the frequency for controller 0. 751 752 case Clock_frequency_lcd_pixel: 753 return get_lcd_source_frequency(0) / get_lcd_pixel_divider(0); 754 755 case Clock_frequency_hdmi: 756 return get_hdmi_source_frequency() / get_hdmi_divider(); 757 758 // NOTE: Consider a better error result. 759 760 default: 761 return 0; 762 } 763 } 764 765 void 766 Cpm_jz4780_chip::set_frequency(enum Clock_frequency_identifiers clock, uint32_t frequency) 767 { 768 switch (clock) 769 { 770 // NOTE: HCLK/AHB0 must be 1.5 (for TFT) or 3 (for STN) times the pixel clock. 771 // NOTE: Here, the actual frequency value is ignored, but should at least be 772 // NOTE: tested. 773 774 case Clock_frequency_lcd: 775 set_lcd_source(0, Source_pll_V); 776 set_lcd_source(1, Source_pll_V); 777 pll_enable(Pll_control_V); 778 break; 779 780 // Unlike the JZ4740, HCLK/AHB0 is used as the device frequency, with the pixel 781 // frequency being based on the selected clock source (SCLK_A, MPLL or VPLL). 782 783 case Clock_frequency_lcd_pixel: 784 785 // Switch to the video PLL and attempt to set the divider. 786 787 set_lcd_pixel_divider(0, get_lcd_source_frequency() / frequency); 788 set_lcd_pixel_divider(1, get_lcd_source_frequency() / frequency); 789 break; 790 791 case Clock_frequency_hdmi: 792 793 // Switch to the video PLL and attempt to set the divider. 794 795 set_hdmi_source(Source_pll_V); 796 pll_enable(Pll_control_V); 797 set_hdmi_divider(get_hdmi_source_frequency() / frequency); 798 break; 799 800 default: 801 break; 802 } 803 } 804 805 // NOTE: Empty method for compatibility. 806 807 void 808 Cpm_jz4780_chip::update_output_frequency() 809 { 810 } 811 812 813 814 // C language interface functions. 815 816 void 817 *jz4780_cpm_init(l4_addr_t cpm_base) 818 { 819 /* Initialise the clock and power management peripheral with the 820 register memory region and a 48MHz EXCLK frequency. */ 821 822 return (void *) new Cpm_jz4780_chip(cpm_base, 48000000, 32768); 823 } 824 825 int 826 jz4780_cpm_have_clock(void *cpm, enum Clock_identifiers clock) 827 { 828 return static_cast<Cpm_jz4780_chip *>(cpm)->have_clock(clock); 829 } 830 831 void 832 jz4780_cpm_start_clock(void *cpm, enum Clock_identifiers clock) 833 { 834 static_cast<Cpm_jz4780_chip *>(cpm)->start_clock(clock); 835 } 836 837 void 838 jz4780_cpm_stop_clock(void *cpm, enum Clock_identifiers clock) 839 { 840 static_cast<Cpm_jz4780_chip *>(cpm)->stop_clock(clock); 841 } 842 843 844 845 uint8_t 846 jz4780_cpm_get_cpu_divider(void *cpm) 847 { 848 return static_cast<Cpm_jz4780_chip *>(cpm)->get_cpu_divider(); 849 } 850 851 uint8_t 852 jz4780_cpm_get_hclock0_divider(void *cpm) 853 { 854 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock0_divider(); 855 } 856 857 uint8_t 858 jz4780_cpm_get_hclock2_divider(void *cpm) 859 { 860 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock2_divider(); 861 } 862 863 uint8_t 864 jz4780_cpm_get_hdmi_divider(void *cpm) 865 { 866 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hdmi_divider(); 867 } 868 869 uint8_t 870 jz4780_cpm_get_lcd_pixel_divider(void *cpm) 871 { 872 return static_cast<Cpm_jz4780_chip *>(cpm)->get_lcd_pixel_divider(); 873 } 874 875 uint8_t 876 jz4780_cpm_get_memory_divider(void *cpm) 877 { 878 return static_cast<Cpm_jz4780_chip *>(cpm)->get_memory_divider(); 879 } 880 881 uint8_t 882 jz4780_cpm_get_pclock_divider(void *cpm) 883 { 884 return static_cast<Cpm_jz4780_chip *>(cpm)->get_pclock_divider(); 885 } 886 887 888 889 uint8_t 890 jz4780_cpm_get_hclock0_source(void *cpm) 891 { 892 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock0_source(); 893 } 894 895 uint8_t 896 jz4780_cpm_get_hclock2_source(void *cpm) 897 { 898 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock2_source(); 899 } 900 901 uint8_t 902 jz4780_cpm_get_hdmi_source(void *cpm) 903 { 904 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hdmi_source(); 905 } 906 907 uint8_t 908 jz4780_cpm_get_lcd_source(void *cpm) 909 { 910 return static_cast<Cpm_jz4780_chip *>(cpm)->get_lcd_source(); 911 } 912 913 uint8_t 914 jz4780_cpm_get_memory_source(void *cpm) 915 { 916 return static_cast<Cpm_jz4780_chip *>(cpm)->get_memory_source(); 917 } 918 919 uint8_t 920 jz4780_cpm_get_pclock_source(void *cpm) 921 { 922 return static_cast<Cpm_jz4780_chip *>(cpm)->get_pclock_source(); 923 } 924 925 void 926 jz4780_cpm_set_pclock_source(void *cpm, uint8_t source) 927 { 928 static_cast<Cpm_jz4780_chip *>(cpm)->set_pclock_source(source); 929 } 930 931 932 933 uint32_t 934 jz4780_cpm_get_hclock0_source_frequency(void *cpm) 935 { 936 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock0_source_frequency(); 937 } 938 939 uint32_t 940 jz4780_cpm_get_hclock2_source_frequency(void *cpm) 941 { 942 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock2_source_frequency(); 943 } 944 945 uint32_t 946 jz4780_cpm_get_hdmi_source_frequency(void *cpm) 947 { 948 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hdmi_source_frequency(); 949 } 950 951 uint32_t 952 jz4780_cpm_get_lcd_source_frequency(void *cpm) 953 { 954 return static_cast<Cpm_jz4780_chip *>(cpm)->get_lcd_source_frequency(); 955 } 956 957 uint32_t 958 jz4780_cpm_get_memory_source_frequency(void *cpm) 959 { 960 return static_cast<Cpm_jz4780_chip *>(cpm)->get_memory_source_frequency(); 961 } 962 963 uint32_t 964 jz4780_cpm_get_pclock_source_frequency(void *cpm) 965 { 966 return static_cast<Cpm_jz4780_chip *>(cpm)->get_pclock_source_frequency(); 967 } 968 969 970 971 uint8_t 972 jz4780_cpm_get_main_source(void *cpm) 973 { 974 return static_cast<Cpm_jz4780_chip *>(cpm)->get_main_source(); 975 } 976 977 uint32_t 978 jz4780_cpm_get_main_frequency(void *cpm) 979 { 980 return static_cast<Cpm_jz4780_chip *>(cpm)->get_main_frequency(); 981 } 982 983 uint32_t 984 jz4780_cpm_get_cpu_frequency(void *cpm) 985 { 986 return static_cast<Cpm_jz4780_chip *>(cpm)->get_cpu_frequency(); 987 } 988 989 uint32_t 990 jz4780_cpm_get_hclock0_frequency(void *cpm) 991 { 992 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock0_frequency(); 993 } 994 995 uint32_t 996 jz4780_cpm_get_hclock2_frequency(void *cpm) 997 { 998 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock2_frequency(); 999 } 1000 1001 uint32_t 1002 jz4780_cpm_get_memory_frequency(void *cpm) 1003 { 1004 return static_cast<Cpm_jz4780_chip *>(cpm)->get_memory_frequency(); 1005 } 1006 1007 uint32_t 1008 jz4780_cpm_get_pclock_frequency(void *cpm) 1009 { 1010 return static_cast<Cpm_jz4780_chip *>(cpm)->get_pclock_frequency(); 1011 } 1012 1013 uint32_t 1014 jz4780_cpm_get_apll_frequency(void *cpm) 1015 { 1016 return static_cast<Cpm_jz4780_chip *>(cpm)->get_apll_frequency(); 1017 } 1018 1019 uint32_t 1020 jz4780_cpm_get_epll_frequency(void *cpm) 1021 { 1022 return static_cast<Cpm_jz4780_chip *>(cpm)->get_epll_frequency(); 1023 } 1024 1025 uint32_t 1026 jz4780_cpm_get_mpll_frequency(void *cpm) 1027 { 1028 return static_cast<Cpm_jz4780_chip *>(cpm)->get_mpll_frequency(); 1029 } 1030 1031 uint32_t 1032 jz4780_cpm_get_vpll_frequency(void *cpm) 1033 { 1034 return static_cast<Cpm_jz4780_chip *>(cpm)->get_vpll_frequency(); 1035 } 1036 1037 1038 1039 uint32_t 1040 jz4780_cpm_get_frequency(void *cpm, enum Clock_frequency_identifiers clock) 1041 { 1042 return static_cast<Cpm_jz4780_chip *>(cpm)->get_frequency(clock); 1043 } 1044 1045 void 1046 jz4780_cpm_set_frequency(void *cpm, enum Clock_frequency_identifiers clock, uint32_t frequency) 1047 { 1048 static_cast<Cpm_jz4780_chip *>(cpm)->set_frequency(clock, frequency); 1049 } 1050 1051 void 1052 jz4780_cpm_set_mpll_parameters(void *cpm, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) 1053 { 1054 static_cast<Cpm_jz4780_chip *>(cpm)->set_pll_parameters(Pll_control_M, multiplier, in_divider, out_divider); 1055 }