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 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, // SMB2 (in CLKGR0) 124 Clock_gate_smb0 = 5, // SMB2 (in CLKGR0) 125 }; 126 127 enum Clock_gate_regs : unsigned 128 { 129 Clock_gate_reg_smb4 = Clock_gate1, 130 Clock_gate_reg_smb3 = Clock_gate1, 131 Clock_gate_reg_smb2 = Clock_gate0, 132 Clock_gate_reg_smb1 = Clock_gate0, 133 Clock_gate_reg_smb0 = Clock_gate0, 134 }; 135 136 enum Divider_bits : unsigned 137 { 138 Ddr_divider_value = 0, // DDRCDR 139 Hdmi_divider_value = 0, // HDMICDR 140 Lcd_divider_value = 0, // LPCDR 141 }; 142 143 enum Lcd_clock_values : unsigned 144 { 145 Lcd_change_enable = 0x10000000, // CE_LCD 146 Lcd_change_busy = 0x08000000, // LCD_BUSY 147 Lcd_clock_stop = 0x04000000, // LCD_STOP 148 }; 149 150 enum Hdmi_divider_values : unsigned 151 { 152 Hdmi_select_mask = 0xc0000000, // HPCS 153 Hdmi_change_enable = 0x20000000, // CE_HDMI 154 Hdmi_change_busy = 0x10000000, // HDMI_BUSY 155 Hdmi_clock_stop = 0x08000000, // HDMI_STOP 156 }; 157 158 159 160 // If implemented as a Hw::Device, various properties would be 161 // initialised in the constructor and obtained from the device tree 162 // definitions. 163 164 Cpm_jz4780_chip::Cpm_jz4780_chip(l4_addr_t addr, uint32_t exclk_freq, uint32_t rtclk_freq) 165 : _exclk_freq(exclk_freq), _rtclk_freq(rtclk_freq) 166 { 167 _regs = new Hw::Mmio_register_block<32>(addr); 168 169 // add_cid("cpm"); 170 // add_cid("cpm-jz4780"); 171 // register_property("exclk_freq", &_exclk_freq); 172 } 173 174 // Clock/timer control. 175 // NOTE: For the time being, assume that the system is configured. 176 177 int 178 Cpm_jz4780_chip::have_clock() 179 { 180 // return !(_regs[Clock_gate] & (1 << Clock_gate_timer)); 181 return 1; 182 } 183 184 void 185 Cpm_jz4780_chip::start_clock() 186 { 187 // _regs[Clock_gate] = _regs[Clock_gate] & ~(1 << Clock_gate_timer); 188 } 189 190 191 192 // Utility methods. 193 194 uint32_t 195 Cpm_jz4780_chip::get_field(uint32_t reg, uint32_t mask, uint8_t shift) 196 { 197 return (_regs[reg] & (mask << shift)) >> shift; 198 } 199 200 void 201 Cpm_jz4780_chip::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) 202 { 203 _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); 204 } 205 206 // General clock divider access. 207 208 uint8_t 209 Cpm_jz4780_chip::_get_divider(uint32_t reg, uint32_t mask, uint8_t shift) 210 { 211 uint8_t d = get_field(reg, mask, shift); 212 213 // NOTE: Value 15 stops the clock, 14 presumably resumes the clock. 214 215 return (d < 14) ? d + 1 : 1; 216 } 217 218 219 220 // PLL control. 221 222 // Return whether the PLL is stable. 223 224 int 225 Cpm_jz4780_chip::have_pll(uint32_t pll_reg) 226 { 227 return _regs[pll_reg] & (1 << Pll_stable); 228 } 229 230 int 231 Cpm_jz4780_chip::pll_enabled(uint32_t pll_reg) 232 { 233 return _regs[pll_reg] & (1 << Pll_enabled); 234 } 235 236 int 237 Cpm_jz4780_chip::pll_bypassed(uint32_t pll_reg) 238 { 239 return _regs[pll_reg] & (1 << Pll_bypassed); 240 } 241 242 void 243 Cpm_jz4780_chip::pll_enable(uint32_t pll_reg) 244 { 245 _regs[pll_reg] = _regs[pll_reg] | (1 << Pll_enabled); 246 while (!(_regs[pll_reg] & (1 << Pll_stable))); 247 } 248 249 void 250 Cpm_jz4780_chip::pll_disable(uint32_t pll_reg) 251 { 252 _regs[pll_reg] = _regs[pll_reg] & ~(1 << Pll_enabled); 253 while (_regs[pll_reg] & (1 << Pll_stable)); 254 } 255 256 // Feedback (13-bit) multiplier. 257 258 uint16_t 259 Cpm_jz4780_chip::get_multiplier(uint32_t pll_reg) 260 { 261 return get_field(pll_reg, 0x1fff, Pll_multiplier) + 1; 262 } 263 264 void 265 Cpm_jz4780_chip::set_multiplier(uint32_t pll_reg, uint16_t multiplier) 266 { 267 set_field(pll_reg, 0x1fff, Pll_multiplier, multiplier - 1); 268 } 269 270 // Input (6-bit) divider. 271 272 uint8_t 273 Cpm_jz4780_chip::get_input_division(uint32_t pll_reg) 274 { 275 return get_field(pll_reg, 0x3f, Pll_input_division) + 1; 276 } 277 278 void 279 Cpm_jz4780_chip::set_input_division(uint32_t pll_reg, uint8_t divider) 280 { 281 set_field(pll_reg, 0x3f, Pll_input_division, divider - 1); 282 } 283 284 // Output divider. 285 286 uint8_t 287 Cpm_jz4780_chip::get_output_division(uint32_t pll_reg) 288 { 289 uint8_t d = get_field(pll_reg, 0x0f, Pll_output_division); 290 291 // Zero yields a division of one. Otherwise enforce even results. 292 293 return d == 0 ? 1 : (d + 1) & 0x0e; 294 } 295 296 void 297 Cpm_jz4780_chip::set_output_division(uint32_t pll_reg, uint8_t divider) 298 { 299 uint8_t d = divider <= 1 ? 0 : (divider & 0x0e) - 1; 300 301 set_field(pll_reg, 0x0f, Pll_output_division, d); 302 } 303 304 uint32_t 305 Cpm_jz4780_chip::get_pll_frequency(uint32_t pll_reg) 306 { 307 // Test for PLL enable and not PLL bypass. 308 309 if (pll_enabled(pll_reg) && !pll_bypassed(pll_reg)) 310 return (_exclk_freq * get_multiplier(pll_reg)) / 311 (get_input_division(pll_reg) * get_output_division(pll_reg)); 312 else 313 return _exclk_freq; 314 } 315 316 void 317 Cpm_jz4780_chip::set_pll_parameters(uint32_t pll_reg, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) 318 { 319 set_multiplier(pll_reg, multiplier); 320 set_input_division(pll_reg, in_divider); 321 set_output_division(pll_reg, out_divider); 322 323 if (pll_enabled(pll_reg) && !pll_bypassed(pll_reg)) 324 while (!have_pll(pll_reg)); 325 } 326 327 328 329 // CPU clock (CCLK) divider. 330 331 uint8_t 332 Cpm_jz4780_chip::get_cpu_divider() 333 { 334 return _get_divider(Clock_control, 0xf, Clock_cpu_divider); 335 } 336 337 // Fast peripheral clock (H0CLK) divider. 338 339 uint8_t 340 Cpm_jz4780_chip::get_hclock0_divider() 341 { 342 return _get_divider(Clock_control, 0xf, Clock_hclock0_divider); 343 } 344 345 // Fast peripheral clock (H2CLK) divider. 346 347 uint8_t 348 Cpm_jz4780_chip::get_hclock2_divider() 349 { 350 return _get_divider(Clock_control, 0xf, Clock_hclock2_divider); 351 } 352 353 // Slow peripheral clock (PCLK) divider. 354 355 uint8_t 356 Cpm_jz4780_chip::get_pclock_divider() 357 { 358 return _get_divider(Clock_control, 0xf, Clock_pclock_divider); 359 } 360 361 // HDMI clock divider. 362 363 uint8_t 364 Cpm_jz4780_chip::get_hdmi_divider() 365 { 366 return get_field(Hdmi_divider, 0xff, Hdmi_divider_value) + 1; 367 } 368 369 // LCD clock (LPCLK) divider for LCD0 or LCD1 pixel clock. 370 371 uint8_t 372 Cpm_jz4780_chip::get_lcd_pixel_divider(uint8_t controller) 373 { 374 return get_field(controller ? Lcd_divider1 : Lcd_divider0, 0xff, Lcd_divider_value) + 1; 375 } 376 377 // Memory clock (DDR_CLK) divider. 378 379 uint8_t 380 Cpm_jz4780_chip::get_memory_divider() 381 { 382 return _get_divider(Ddr_divider, 0xf, Ddr_divider_value); 383 } 384 385 // HDMI clock divider. 386 387 void 388 Cpm_jz4780_chip::set_hdmi_divider(uint16_t division) 389 { 390 if ((division < 1) || (division > 256)) 391 return; 392 393 // Enable change. 394 395 _regs[Hdmi_divider] = _regs[Hdmi_divider] | Hdmi_change_enable; 396 397 // Set the divider. 398 399 set_field(Hdmi_divider, 0xff, Hdmi_divider_value, division - 1); 400 401 // Restart clock and disable change. 402 403 while (_regs[Hdmi_divider] & Hdmi_change_busy); 404 _regs[Hdmi_divider] = _regs[Hdmi_divider] & ~Hdmi_change_enable; 405 } 406 407 // LCD pixel clock divider. 408 // NOTE: This only supports the first LCD peripheral. 409 410 void 411 Cpm_jz4780_chip::set_lcd_pixel_divider(uint8_t controller, uint16_t division) 412 { 413 uint32_t divider = controller ? Lcd_divider1 : Lcd_divider0; 414 415 if ((division < 1) || (division > 256)) 416 return; 417 418 // Enable change. 419 420 _regs[divider] = _regs[divider] | Lcd_change_enable; 421 422 // Set the divider. 423 424 set_field(divider, 0xff, Lcd_divider_value, division - 1); 425 426 // Restart clock and disable change. 427 428 while (_regs[divider] & Lcd_change_busy); 429 _regs[divider] = _regs[divider] & ~Lcd_change_enable; 430 } 431 432 433 434 // Clock gating control. 435 436 void 437 Cpm_jz4780_chip::start_hdmi() 438 { 439 _regs[Clock_gate1] = _regs[Clock_gate1] & ~(1 << Clock_gate_hdmi); 440 } 441 442 void 443 Cpm_jz4780_chip::stop_hdmi() 444 { 445 _regs[Clock_gate1] = _regs[Clock_gate1] | (1 << Clock_gate_hdmi); 446 } 447 448 void 449 Cpm_jz4780_chip::start_lcd() 450 { 451 // JZ4780 apparently needs LCD0/TVE to be ungated for the LCD peripheral to 452 // work. The Linux 3.0.8 vendor kernel reveals that the TVE clock is actually 453 // LCD0 and that the LCD clock is actually LCD1. 454 455 // According to the 3.0.8 kernel, LCD1 is the parent of LCD0. However, LCD0 456 // does seem to operate without LCD1 enabled. 457 458 _regs[Clock_gate0] = _regs[Clock_gate0] & ~(1 << Clock_gate_lcd1); 459 _regs[Clock_gate0] = _regs[Clock_gate0] & ~(1 << Clock_gate_lcd0); 460 } 461 462 void 463 Cpm_jz4780_chip::stop_lcd() 464 { 465 _regs[Clock_gate0] = _regs[Clock_gate0] | (1 << Clock_gate_lcd1); 466 _regs[Clock_gate0] = _regs[Clock_gate0] | (1 << Clock_gate_lcd0); 467 } 468 469 void 470 Cpm_jz4780_chip::_update_i2c(uint8_t channel, int enable) 471 { 472 uint8_t bit; 473 uint32_t reg; 474 switch (channel) 475 { 476 case 0: bit = Clock_gate_smb0; reg = Clock_gate_reg_smb0; break; 477 case 1: bit = Clock_gate_smb1; reg = Clock_gate_reg_smb1; break; 478 case 2: bit = Clock_gate_smb2; reg = Clock_gate_reg_smb2; break; 479 case 3: bit = Clock_gate_smb3; reg = Clock_gate_reg_smb3; break; 480 case 4: bit = Clock_gate_smb4; reg = Clock_gate_reg_smb4; break; 481 default: return; 482 } 483 if (enable) 484 _regs[reg] = _regs[reg] & ~(1 << bit); 485 else 486 _regs[reg] = _regs[reg] | (1 << bit); 487 } 488 489 void 490 Cpm_jz4780_chip::start_i2c(uint8_t channel) 491 { 492 _update_i2c(channel, 1); 493 } 494 495 void 496 Cpm_jz4780_chip::stop_i2c(uint8_t channel) 497 { 498 _update_i2c(channel, 0); 499 } 500 501 502 503 // Clock sources. 504 505 uint8_t 506 Cpm_jz4780_chip::get_memory_source() 507 { 508 return get_field(Ddr_divider, 0x3, Clock_source_ddr); 509 } 510 511 uint32_t 512 Cpm_jz4780_chip::get_memory_source_frequency() 513 { 514 switch (get_memory_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 default: 521 return 0; 522 } 523 } 524 525 uint8_t 526 Cpm_jz4780_chip::get_cpu_source() 527 { 528 return get_field(Clock_control, 0x3, Clock_source_cpu); 529 } 530 531 uint32_t 532 Cpm_jz4780_chip::get_cpu_source_frequency() 533 { 534 switch (get_cpu_source()) 535 { 536 case Source_mux_main: 537 return get_main_frequency(); 538 case Source_mux_pll_M: 539 return get_pll_frequency(Pll_control_M); 540 case Source_mux_pll_E: 541 return get_pll_frequency(Pll_control_E); 542 default: 543 return 0; 544 } 545 } 546 547 uint8_t 548 Cpm_jz4780_chip::get_hclock0_source() 549 { 550 return get_field(Clock_control, 0x3, Clock_source_hclock0); 551 } 552 553 uint32_t 554 Cpm_jz4780_chip::get_hclock0_source_frequency() 555 { 556 switch (get_hclock0_source()) 557 { 558 case Source_mux_main: 559 return get_main_frequency(); 560 case Source_mux_pll_M: 561 return get_pll_frequency(Pll_control_M); 562 case Source_mux_pll_E: 563 return get_pll_frequency(Pll_control_E); 564 default: 565 return 0; 566 } 567 } 568 569 uint8_t 570 Cpm_jz4780_chip::get_hclock2_source() 571 { 572 return get_field(Clock_control, 0x3, Clock_source_hclock2); 573 } 574 575 uint32_t 576 Cpm_jz4780_chip::get_hclock2_source_frequency() 577 { 578 switch (get_hclock2_source()) 579 { 580 case Source_mux_main: 581 return get_main_frequency(); 582 case Source_mux_pll_M: 583 return get_pll_frequency(Pll_control_M); 584 case Source_mux_realtime: 585 return _rtclk_freq; // "TCK" in the manual, RTCLK in Linux driver code 586 default: 587 return 0; 588 } 589 } 590 591 void 592 Cpm_jz4780_chip::set_hclock2_source(uint8_t source) 593 { 594 set_field(Clock_control, 0x3, Clock_source_hclock2, source); 595 } 596 597 uint8_t 598 Cpm_jz4780_chip::get_hdmi_source() 599 { 600 return get_field(Hdmi_divider, 0x3, Clock_source_hdmi); 601 } 602 603 uint32_t 604 Cpm_jz4780_chip::get_hdmi_source_frequency() 605 { 606 switch (get_hdmi_source()) 607 { 608 case Source_main: 609 return get_main_frequency(); 610 case Source_pll_M: 611 return get_pll_frequency(Pll_control_M); 612 case Source_pll_V: 613 return get_pll_frequency(Pll_control_V); 614 default: 615 return 0; 616 } 617 } 618 619 void 620 Cpm_jz4780_chip::set_hdmi_source(uint8_t source) 621 { 622 // Stop clock and enable change. 623 624 _regs[Hdmi_divider] = _regs[Hdmi_divider] | Hdmi_change_enable | Hdmi_clock_stop; 625 626 // Set the source. 627 628 set_field(Hdmi_divider, 0x03, Clock_source_hdmi, source); 629 630 // Restart clock and disable change. 631 632 while (_regs[Hdmi_divider] & Hdmi_change_busy); 633 _regs[Hdmi_divider] = _regs[Hdmi_divider] & ~(Hdmi_change_enable | Hdmi_clock_stop); 634 } 635 636 uint8_t 637 Cpm_jz4780_chip::get_lcd_source(uint8_t controller) 638 { 639 return get_field(controller ? Lcd_divider1 : Lcd_divider0, 0x3, Clock_source_lcd); 640 } 641 642 uint32_t 643 Cpm_jz4780_chip::get_lcd_source_frequency(uint8_t controller) 644 { 645 switch (get_lcd_source(controller)) 646 { 647 case Source_main: 648 return get_main_frequency(); 649 case Source_pll_M: 650 return get_pll_frequency(Pll_control_M); 651 case Source_pll_V: 652 return get_pll_frequency(Pll_control_V); 653 default: 654 return 0; 655 } 656 } 657 658 void 659 Cpm_jz4780_chip::set_lcd_source(uint8_t controller, uint8_t source) 660 { 661 uint32_t divider = controller ? Lcd_divider1 : Lcd_divider0; 662 663 // Stop clock and enable change. 664 665 _regs[divider] = _regs[divider] | Lcd_change_enable | Lcd_clock_stop; 666 667 // Set the source. 668 669 set_field(divider, 0x03, Clock_source_lcd, source); 670 671 // Restart clock and disable change. 672 673 while (_regs[divider] & Lcd_change_busy); 674 _regs[divider] = _regs[divider] & ~(Lcd_change_enable | Lcd_clock_stop); 675 } 676 677 uint8_t 678 Cpm_jz4780_chip::get_pclock_source() 679 { 680 return get_hclock2_source(); 681 } 682 683 uint32_t 684 Cpm_jz4780_chip::get_pclock_source_frequency() 685 { 686 return get_hclock2_source_frequency(); 687 } 688 689 void 690 Cpm_jz4780_chip::set_pclock_source(uint8_t source) 691 { 692 set_hclock2_source(source); 693 } 694 695 696 697 // Source frequency, used by various clock sources. 698 699 uint8_t 700 Cpm_jz4780_chip::get_main_source() 701 { 702 return get_field(Clock_control, 0x3, Clock_source_main); 703 } 704 705 uint32_t 706 Cpm_jz4780_chip::get_main_frequency() 707 { 708 switch (get_main_source()) 709 { 710 case Source_pll_A: 711 return get_pll_frequency(Pll_control_A); 712 case Source_external: 713 return _exclk_freq; 714 case Source_realtime: 715 return _rtclk_freq; 716 default: 717 return 0; 718 } 719 } 720 721 // Clock frequency for the CPU. 722 723 uint32_t 724 Cpm_jz4780_chip::get_cpu_frequency() 725 { 726 return get_cpu_source_frequency() / get_cpu_divider(); 727 } 728 729 // Clock frequency for fast peripherals. 730 731 uint32_t 732 Cpm_jz4780_chip::get_hclock0_frequency() 733 { 734 return get_hclock0_source_frequency() / get_hclock0_divider(); 735 } 736 737 // Clock frequency for fast peripherals. 738 739 uint32_t 740 Cpm_jz4780_chip::get_hclock2_frequency() 741 { 742 return get_hclock2_source_frequency() / get_hclock2_divider(); 743 } 744 745 // Clock frequency for slow peripherals. 746 747 uint32_t 748 Cpm_jz4780_chip::get_pclock_frequency() 749 { 750 return get_pclock_source_frequency() / get_pclock_divider(); 751 } 752 753 // Clock frequency for the HDMI peripheral. 754 755 uint32_t 756 Cpm_jz4780_chip::get_hdmi_frequency() 757 { 758 return get_hdmi_source_frequency() / get_hdmi_divider(); 759 } 760 761 // Clock frequency for the LCD0 or LCD1 controller. 762 763 uint32_t 764 Cpm_jz4780_chip::get_lcd_pixel_frequency(uint8_t controller) 765 { 766 return get_lcd_source_frequency(controller) / get_lcd_pixel_divider(controller); 767 } 768 769 // Clock frequency for the memory. 770 771 uint32_t 772 Cpm_jz4780_chip::get_memory_frequency() 773 { 774 return get_memory_source_frequency() / get_memory_divider(); 775 } 776 777 uint32_t 778 Cpm_jz4780_chip::get_apll_frequency() 779 { 780 return get_pll_frequency(Pll_control_A); 781 } 782 783 uint32_t 784 Cpm_jz4780_chip::get_epll_frequency() 785 { 786 return get_pll_frequency(Pll_control_E); 787 } 788 789 uint32_t 790 Cpm_jz4780_chip::get_mpll_frequency() 791 { 792 return get_pll_frequency(Pll_control_M); 793 } 794 795 uint32_t 796 Cpm_jz4780_chip::get_vpll_frequency() 797 { 798 return get_pll_frequency(Pll_control_V); 799 } 800 801 802 803 void 804 Cpm_jz4780_chip::set_hdmi_frequency(uint32_t pclk) 805 { 806 // Switch to the video PLL and attempt to set the divider. 807 808 set_hdmi_source(Source_pll_V); 809 pll_enable(Pll_control_V); 810 set_hdmi_divider(get_hdmi_source_frequency() / pclk); 811 } 812 813 // Set the pixel frequency. 814 // Unlike the jz4740, HCLK/AHB0 is used as the device frequency, with the pixel 815 // frequency being based on the selected clock source (SCLK_A, MPLL or VPLL). 816 817 void 818 Cpm_jz4780_chip::set_lcd_pixel_frequency(uint32_t pclk) 819 { 820 // Switch to the video PLL and attempt to set the divider. 821 822 set_lcd_source(0, Source_pll_V); 823 set_lcd_source(1, Source_pll_V); 824 pll_enable(Pll_control_V); 825 set_lcd_pixel_divider(0, get_lcd_source_frequency() / pclk); 826 set_lcd_pixel_divider(1, get_lcd_source_frequency() / pclk); 827 } 828 829 // NOTE: Compatibility method. Probably needs reviewing. 830 // NOTE: HCLK/AHB0 must be 1.5 (for TFT) or 3 (for STN) times the pixel clock. 831 832 void 833 Cpm_jz4780_chip::set_lcd_frequencies(uint32_t pclk, uint8_t ratio) 834 { 835 (void) ratio; 836 set_lcd_pixel_frequency(pclk); 837 } 838 839 // NOTE: Empty method for compatibility. 840 // NOTE: It could potentially be combined with start_lcd. 841 842 void 843 Cpm_jz4780_chip::update_output_frequency() 844 { 845 } 846 847 848 849 // C language interface functions. 850 851 void 852 *jz4780_cpm_init(l4_addr_t cpm_base) 853 { 854 /* Initialise the clock and power management peripheral with the 855 register memory region and a 48MHz EXCLK frequency. */ 856 857 return (void *) new Cpm_jz4780_chip(cpm_base, 48000000, 32768); 858 } 859 860 int 861 jz4780_cpm_have_clock(void *cpm) 862 { 863 return static_cast<Cpm_jz4780_chip *>(cpm)->have_clock(); 864 } 865 866 void 867 jz4780_cpm_start_clock(void *cpm) 868 { 869 static_cast<Cpm_jz4780_chip *>(cpm)->start_clock(); 870 } 871 872 873 874 void 875 jz4780_cpm_start_hdmi(void *cpm) 876 { 877 static_cast<Cpm_jz4780_chip *>(cpm)->start_hdmi(); 878 } 879 880 void 881 jz4780_cpm_stop_hdmi(void *cpm) 882 { 883 static_cast<Cpm_jz4780_chip *>(cpm)->stop_hdmi(); 884 } 885 886 void 887 jz4780_cpm_start_lcd(void *cpm) 888 { 889 static_cast<Cpm_jz4780_chip *>(cpm)->start_lcd(); 890 } 891 892 void 893 jz4780_cpm_stop_lcd(void *cpm) 894 { 895 static_cast<Cpm_jz4780_chip *>(cpm)->stop_lcd(); 896 } 897 898 899 900 uint8_t 901 jz4780_cpm_get_cpu_divider(void *cpm) 902 { 903 return static_cast<Cpm_jz4780_chip *>(cpm)->get_cpu_divider(); 904 } 905 906 uint8_t 907 jz4780_cpm_get_hclock0_divider(void *cpm) 908 { 909 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock0_divider(); 910 } 911 912 uint8_t 913 jz4780_cpm_get_hclock2_divider(void *cpm) 914 { 915 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock2_divider(); 916 } 917 918 uint8_t 919 jz4780_cpm_get_hdmi_divider(void *cpm) 920 { 921 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hdmi_divider(); 922 } 923 924 uint8_t 925 jz4780_cpm_get_lcd_pixel_divider(void *cpm) 926 { 927 return static_cast<Cpm_jz4780_chip *>(cpm)->get_lcd_pixel_divider(); 928 } 929 930 uint8_t 931 jz4780_cpm_get_memory_divider(void *cpm) 932 { 933 return static_cast<Cpm_jz4780_chip *>(cpm)->get_memory_divider(); 934 } 935 936 uint8_t 937 jz4780_cpm_get_pclock_divider(void *cpm) 938 { 939 return static_cast<Cpm_jz4780_chip *>(cpm)->get_pclock_divider(); 940 } 941 942 943 944 uint8_t 945 jz4780_cpm_get_hclock0_source(void *cpm) 946 { 947 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock0_source(); 948 } 949 950 uint8_t 951 jz4780_cpm_get_hclock2_source(void *cpm) 952 { 953 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock2_source(); 954 } 955 956 uint8_t 957 jz4780_cpm_get_hdmi_source(void *cpm) 958 { 959 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hdmi_source(); 960 } 961 962 uint8_t 963 jz4780_cpm_get_lcd_source(void *cpm) 964 { 965 return static_cast<Cpm_jz4780_chip *>(cpm)->get_lcd_source(); 966 } 967 968 uint8_t 969 jz4780_cpm_get_memory_source(void *cpm) 970 { 971 return static_cast<Cpm_jz4780_chip *>(cpm)->get_memory_source(); 972 } 973 974 uint8_t 975 jz4780_cpm_get_pclock_source(void *cpm) 976 { 977 return static_cast<Cpm_jz4780_chip *>(cpm)->get_pclock_source(); 978 } 979 980 void 981 jz4780_cpm_set_pclock_source(void *cpm, uint8_t source) 982 { 983 static_cast<Cpm_jz4780_chip *>(cpm)->set_pclock_source(source); 984 } 985 986 987 988 uint32_t 989 jz4780_cpm_get_hclock0_source_frequency(void *cpm) 990 { 991 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock0_source_frequency(); 992 } 993 994 uint32_t 995 jz4780_cpm_get_hclock2_source_frequency(void *cpm) 996 { 997 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock2_source_frequency(); 998 } 999 1000 uint32_t 1001 jz4780_cpm_get_hdmi_source_frequency(void *cpm) 1002 { 1003 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hdmi_source_frequency(); 1004 } 1005 1006 uint32_t 1007 jz4780_cpm_get_lcd_source_frequency(void *cpm) 1008 { 1009 return static_cast<Cpm_jz4780_chip *>(cpm)->get_lcd_source_frequency(); 1010 } 1011 1012 uint32_t 1013 jz4780_cpm_get_memory_source_frequency(void *cpm) 1014 { 1015 return static_cast<Cpm_jz4780_chip *>(cpm)->get_memory_source_frequency(); 1016 } 1017 1018 uint32_t 1019 jz4780_cpm_get_pclock_source_frequency(void *cpm) 1020 { 1021 return static_cast<Cpm_jz4780_chip *>(cpm)->get_pclock_source_frequency(); 1022 } 1023 1024 1025 1026 uint8_t 1027 jz4780_cpm_get_main_source(void *cpm) 1028 { 1029 return static_cast<Cpm_jz4780_chip *>(cpm)->get_main_source(); 1030 } 1031 1032 uint32_t 1033 jz4780_cpm_get_main_frequency(void *cpm) 1034 { 1035 return static_cast<Cpm_jz4780_chip *>(cpm)->get_main_frequency(); 1036 } 1037 1038 uint32_t 1039 jz4780_cpm_get_cpu_frequency(void *cpm) 1040 { 1041 return static_cast<Cpm_jz4780_chip *>(cpm)->get_cpu_frequency(); 1042 } 1043 1044 uint32_t 1045 jz4780_cpm_get_hclock0_frequency(void *cpm) 1046 { 1047 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock0_frequency(); 1048 } 1049 1050 uint32_t 1051 jz4780_cpm_get_hclock2_frequency(void *cpm) 1052 { 1053 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hclock2_frequency(); 1054 } 1055 1056 uint32_t 1057 jz4780_cpm_get_hdmi_frequency(void *cpm) 1058 { 1059 return static_cast<Cpm_jz4780_chip *>(cpm)->get_hdmi_frequency(); 1060 } 1061 1062 uint32_t 1063 jz4780_cpm_get_lcd_pixel_frequency(void *cpm) 1064 { 1065 return static_cast<Cpm_jz4780_chip *>(cpm)->get_lcd_pixel_frequency(); 1066 } 1067 1068 uint32_t 1069 jz4780_cpm_get_memory_frequency(void *cpm) 1070 { 1071 return static_cast<Cpm_jz4780_chip *>(cpm)->get_memory_frequency(); 1072 } 1073 1074 uint32_t 1075 jz4780_cpm_get_pclock_frequency(void *cpm) 1076 { 1077 return static_cast<Cpm_jz4780_chip *>(cpm)->get_pclock_frequency(); 1078 } 1079 1080 uint32_t 1081 jz4780_cpm_get_apll_frequency(void *cpm) 1082 { 1083 return static_cast<Cpm_jz4780_chip *>(cpm)->get_apll_frequency(); 1084 } 1085 1086 uint32_t 1087 jz4780_cpm_get_epll_frequency(void *cpm) 1088 { 1089 return static_cast<Cpm_jz4780_chip *>(cpm)->get_epll_frequency(); 1090 } 1091 1092 uint32_t 1093 jz4780_cpm_get_mpll_frequency(void *cpm) 1094 { 1095 return static_cast<Cpm_jz4780_chip *>(cpm)->get_mpll_frequency(); 1096 } 1097 1098 uint32_t 1099 jz4780_cpm_get_vpll_frequency(void *cpm) 1100 { 1101 return static_cast<Cpm_jz4780_chip *>(cpm)->get_vpll_frequency(); 1102 } 1103 1104 1105 1106 void 1107 jz4780_cpm_set_hdmi_frequency(void *cpm, uint32_t pclk) 1108 { 1109 static_cast<Cpm_jz4780_chip *>(cpm)->set_hdmi_frequency(pclk); 1110 } 1111 1112 void 1113 jz4780_cpm_set_lcd_pixel_frequency(void *cpm, uint32_t pclk) 1114 { 1115 static_cast<Cpm_jz4780_chip *>(cpm)->set_lcd_pixel_frequency(pclk); 1116 } 1117 1118 void 1119 jz4780_cpm_set_lcd_frequencies(void *cpm, uint32_t pclk, uint8_t ratio) 1120 { 1121 static_cast<Cpm_jz4780_chip *>(cpm)->set_lcd_frequencies(pclk, ratio); 1122 } 1123 1124 void 1125 jz4780_cpm_set_mpll_parameters(void *cpm, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) 1126 { 1127 static_cast<Cpm_jz4780_chip *>(cpm)->set_pll_parameters(Pll_control_M, multiplier, in_divider, out_divider); 1128 }