1 /* 2 * Clock and power management. This exposes the combined functionality 3 * provided by the jz4730. The power management functionality could be exposed 4 * using a separate driver. 5 * 6 * Copyright (C) 2017, 2018, 2020, 2021, 2023 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-jz4730.h" 26 27 28 29 enum Regs : unsigned 30 { 31 Clock_control = 0x000, // CFCR (CPCCR in JZ4740) 32 Pll_control = 0x010, // PLCR1 (CPPCR in JZ4740) 33 Clock_gate = 0x020, // MSCR (CLKGR in JZ4740) 34 Sleep_control = 0x024, // SCR 35 Lcd_divider = 0x060, // CFCR2 36 }; 37 38 enum Clock_bits : unsigned 39 { 40 Clock_change_enable = 20, // UPE 41 Clock_memory_divider = 16, // MFR 42 Clock_lcd_divider = 12, // LFR 43 Clock_pclock_divider = 8, // PFR (slow APB peripherals) 44 Clock_hclock_divider = 4, // SFR (fast AHB peripherals) 45 Clock_cpu_divider = 0, // IFR 46 }; 47 48 enum Pll_bits : unsigned 49 { 50 Pll_multiplier = 23, // PLL1FD 51 Pll_input_division = 18, // PLL1RD 52 Pll_output_division = 16, // PLL1OD 53 Pll_stable = 10, // PLL1S 54 Pll_bypassed = 9, // PLL1BP 55 Pll_enabled = 8, // PLL1EN 56 }; 57 58 enum Clock_gate_bits : unsigned 59 { 60 Clock_gate_uprt = 25, 61 Clock_gate_udc = 24, 62 Clock_gate_cim = 23, 63 Clock_gate_kbc = 22, 64 Clock_gate_emac = 21, 65 Clock_gate_uart3 = 20, 66 Clock_gate_aic_bitclk = 18, 67 Clock_gate_scc = 14, 68 Clock_gate_msc = 13, 69 Clock_gate_ssi = 12, 70 Clock_gate_pwm1 = 11, 71 Clock_gate_pwm0 = 10, 72 Clock_gate_aic_pclk = 9, 73 Clock_gate_i2c = 8, 74 Clock_gate_lcd = 7, 75 Clock_gate_uhc = 6, 76 Clock_gate_dmac = 5, 77 Clock_gate_timer = 3, 78 Clock_gate_uart2 = 2, 79 Clock_gate_uart1 = 1, 80 Clock_gate_uart0 = 0, 81 }; 82 83 enum Lcd_divider_bits : unsigned 84 { 85 Lcd_divider_value = 0, // PIXFR (in CFCR2) 86 }; 87 88 89 90 // If implemented as a Hw::Device, various properties would be 91 // initialised in the constructor and obtained from the device tree 92 // definitions. 93 94 Cpm_jz4730_chip::Cpm_jz4730_chip(l4_addr_t addr, uint32_t exclk_freq) 95 : _exclk_freq(exclk_freq) 96 { 97 _regs = new Hw::Mmio_register_block<32>(addr); 98 99 // add_cid("cpm"); 100 // add_cid("cpm-jz4730"); 101 // register_property("exclk_freq", &_exclk_freq); 102 } 103 104 // Clock/timer control. 105 106 uint32_t 107 Cpm_jz4730_chip::get_clock_gate_value(enum Clock_identifiers clock) 108 { 109 switch (clock) 110 { 111 case Clock_uprt: return (1 << Clock_gate_uprt); 112 case Clock_udc: return (1 << Clock_gate_udc); 113 case Clock_cim: return (1 << Clock_gate_cim); 114 case Clock_kbc: return (1 << Clock_gate_kbc); 115 case Clock_emac: return (1 << Clock_gate_emac); 116 case Clock_uart3: return (1 << Clock_gate_uart3); 117 case Clock_aic_bitclk: return (1 << Clock_gate_aic_bitclk); 118 case Clock_scc: return (1 << Clock_gate_scc); 119 case Clock_msc0: return (1 << Clock_gate_msc); 120 case Clock_ssi: return (1 << Clock_gate_ssi); 121 case Clock_pwm1: return (1 << Clock_gate_pwm1); 122 case Clock_pwm0: return (1 << Clock_gate_pwm0); 123 case Clock_aic_pclk: return (1 << Clock_gate_aic_pclk); 124 case Clock_i2c0: return (1 << Clock_gate_i2c); 125 case Clock_lcd: return (1 << Clock_gate_lcd); 126 case Clock_uhc: return (1 << Clock_gate_uhc); 127 case Clock_dma: return (1 << Clock_gate_dmac); 128 case Clock_timer: return (1 << Clock_gate_timer); 129 case Clock_uart2: return (1 << Clock_gate_uart2); 130 case Clock_uart1: return (1 << Clock_gate_uart1); 131 case Clock_uart0: return (1 << Clock_gate_uart0); 132 default: return 0; 133 } 134 } 135 136 int 137 Cpm_jz4730_chip::have_clock(enum Clock_identifiers clock) 138 { 139 return !(_regs[Clock_gate] & get_clock_gate_value(clock)); 140 } 141 142 void 143 Cpm_jz4730_chip::start_clock(enum Clock_identifiers clock) 144 { 145 _regs[Clock_gate] = _regs[Clock_gate] & ~get_clock_gate_value(clock); 146 } 147 148 void 149 Cpm_jz4730_chip::stop_clock(enum Clock_identifiers clock) 150 { 151 _regs[Clock_gate] = _regs[Clock_gate] | get_clock_gate_value(clock); 152 } 153 154 // PLL control. 155 156 // Return whether the PLL is stable. 157 158 int 159 Cpm_jz4730_chip::have_pll() 160 { 161 return _regs[Pll_control] & (1 << Pll_stable); 162 } 163 164 int 165 Cpm_jz4730_chip::pll_enabled() 166 { 167 return _regs[Pll_control] & (1 << Pll_enabled); 168 } 169 170 int 171 Cpm_jz4730_chip::pll_bypassed() 172 { 173 return _regs[Pll_control] & (1 << Pll_bypassed); 174 } 175 176 // Feedback (9-bit) multiplier. 177 178 uint16_t 179 Cpm_jz4730_chip::get_multiplier() 180 { 181 return ((_regs[Pll_control] & (0x1ff << Pll_multiplier)) >> Pll_multiplier) + 2; 182 } 183 184 // Input (5-bit) divider. 185 186 uint8_t 187 Cpm_jz4730_chip::get_input_division() 188 { 189 return ((_regs[Pll_control] & (0x1f << Pll_input_division)) >> Pll_input_division) + 2; 190 } 191 192 // Output divider. 193 194 static uint8_t od[4] = {1, 2, 2, 4}; 195 196 uint8_t 197 Cpm_jz4730_chip::get_output_division() 198 { 199 return od[(_regs[Pll_control] & (0x03 << Pll_output_division)) >> Pll_output_division]; 200 } 201 202 // General clock divider. 203 204 static uint8_t cd[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; 205 206 uint8_t 207 Cpm_jz4730_chip::_get_divider(uint32_t reg, uint32_t mask, uint8_t shift) 208 { 209 uint8_t d = (_regs[reg] & mask) >> shift; 210 return (d < 10) ? cd[d] : 1; 211 } 212 213 // CPU clock (CCLK) divider. 214 215 uint8_t 216 Cpm_jz4730_chip::get_cpu_divider() 217 { 218 return _get_divider(Clock_control, 0xf << Clock_cpu_divider, Clock_cpu_divider); 219 } 220 221 // Fast peripheral clock (HCLK) divider. 222 223 uint8_t 224 Cpm_jz4730_chip::get_hclock_divider() 225 { 226 return _get_divider(Clock_control, 0xf << Clock_hclock_divider, Clock_hclock_divider); 227 } 228 229 // Slow peripheral clock (PCLK) divider. 230 231 uint8_t 232 Cpm_jz4730_chip::get_pclock_divider() 233 { 234 return _get_divider(Clock_control, 0xf << Clock_pclock_divider, Clock_pclock_divider); 235 } 236 237 // Memory clock (MCLK) divider. 238 239 uint8_t 240 Cpm_jz4730_chip::get_memory_divider() 241 { 242 return _get_divider(Clock_control, 0xf << Clock_memory_divider, Clock_memory_divider); 243 } 244 245 // Clock source divider for MSC, I2S, LCD and USB. 246 247 uint8_t 248 Cpm_jz4730_chip::get_source_divider() 249 { 250 return 1; 251 } 252 253 // LCD device clock divider. 254 255 void 256 Cpm_jz4730_chip::set_lcd_device_divider(uint8_t division) 257 { 258 if (division == 0) 259 division = 1; 260 261 // NOTE: The vendor code (clock.c) bounds the divider at 16, but maybe this is 262 // NOTE: confused with the width of the bitfield which actually contains an 263 // NOTE: index for the clock divider value array (cd). 264 265 else if (division > 16) 266 division = 16; 267 268 // Obtain the divider or closest higher divider. 269 270 int i; 271 272 for (i = 0; i < 10; i++) 273 if (cd[i] >= division) break; 274 275 _regs[Clock_control] = _regs[Clock_control] | (1 << Clock_change_enable); 276 277 _regs[Clock_control] = (_regs[Clock_control] & ~(0xf << Clock_lcd_divider)) | 278 (cd[i] << Clock_lcd_divider); 279 280 _regs[Clock_control] = _regs[Clock_control] & ~(1 << Clock_change_enable); 281 } 282 283 // LCD pixel clock divider. 284 285 uint16_t 286 Cpm_jz4730_chip::get_lcd_pixel_divider() 287 { 288 return (_regs[Lcd_divider] >> Lcd_divider_value) + 1; 289 } 290 291 void 292 Cpm_jz4730_chip::set_lcd_pixel_divider(uint16_t division) 293 { 294 if (division == 0) 295 division = 1; 296 else if (division > 512) 297 division = 512; 298 299 _regs[Clock_control] = _regs[Clock_control] | (1 << Clock_change_enable); 300 301 _regs[Lcd_divider] = (_regs[Lcd_divider] & ~(0x1ff << Lcd_divider_value)) | 302 ((division - 1) << Lcd_divider_value); 303 304 _regs[Clock_control] = _regs[Clock_control] & ~(1 << Clock_change_enable); 305 } 306 307 308 309 uint32_t 310 Cpm_jz4730_chip::get_frequency(enum Clock_identifiers clock) 311 { 312 if (clock == Clock_lcd_pixel0) 313 return get_source_frequency() / get_lcd_pixel_divider(); 314 315 // NOTE: Consider a better error result. 316 return 0; 317 } 318 319 void 320 Cpm_jz4730_chip::set_frequency(enum Clock_identifiers clock, uint32_t frequency) 321 { 322 uint32_t out = get_source_frequency(); 323 324 switch (clock) 325 { 326 // Limit the device frequency to 150MHz. 327 328 case Clock_lcd: 329 if (frequency > 150000000) 330 frequency = 150000000; 331 set_lcd_device_divider(out / frequency); 332 break; 333 334 case Clock_lcd_pixel0: 335 set_lcd_pixel_divider(out / frequency); 336 break; 337 338 default: 339 break; 340 } 341 } 342 343 344 345 uint32_t 346 Cpm_jz4730_chip::get_pll_frequency() 347 { 348 // Test for PLL enable and not PLL bypass. 349 350 if (pll_enabled() && !pll_bypassed()) 351 return (_exclk_freq * get_multiplier()) / 352 (get_input_division() * get_output_division()); 353 else 354 return _exclk_freq; 355 } 356 357 // Clock frequency for MSC, I2S, LCD and USB. 358 359 uint32_t 360 Cpm_jz4730_chip::get_source_frequency() 361 { 362 return get_pll_frequency() / get_source_divider(); 363 } 364 365 // Clock frequency for the CPU. 366 367 uint32_t Cpm_jz4730_chip::get_cpu_frequency() 368 { 369 if (pll_enabled()) 370 return get_pll_frequency() / get_cpu_divider(); 371 else 372 return _exclk_freq; 373 } 374 375 // Clock frequency for fast peripherals. 376 377 uint32_t 378 Cpm_jz4730_chip::get_hclock_frequency() 379 { 380 if (pll_enabled()) 381 return get_pll_frequency() / get_hclock_divider(); 382 else 383 return _exclk_freq; 384 } 385 386 // Clock frequency for slow peripherals. 387 388 uint32_t 389 Cpm_jz4730_chip::get_pclock_frequency() 390 { 391 if (pll_enabled()) 392 return get_pll_frequency() / get_pclock_divider(); 393 else 394 return _exclk_freq; 395 } 396 397 // Clock frequency for the memory. 398 399 uint32_t 400 Cpm_jz4730_chip::get_memory_frequency() 401 { 402 if (pll_enabled()) 403 return get_pll_frequency() / get_memory_divider(); 404 else 405 return _exclk_freq; 406 } 407 408 409 410 // C language interface functions. 411 412 void 413 *jz4730_cpm_init(l4_addr_t cpm_base) 414 { 415 /* Initialise the clock and power management peripheral with the 416 register memory region and a 3.6864 MHz EXCLK frequency. */ 417 418 return (void *) new Cpm_jz4730_chip(cpm_base, 3686400); 419 } 420 421 int 422 jz4730_cpm_have_clock(void *cpm, enum Clock_identifiers clock) 423 { 424 return static_cast<Cpm_jz4730_chip *>(cpm)->have_clock(clock); 425 } 426 427 void 428 jz4730_cpm_start_clock(void *cpm, enum Clock_identifiers clock) 429 { 430 static_cast<Cpm_jz4730_chip *>(cpm)->start_clock(clock); 431 } 432 433 void 434 jz4730_cpm_stop_clock(void *cpm, enum Clock_identifiers clock) 435 { 436 static_cast<Cpm_jz4730_chip *>(cpm)->stop_clock(clock); 437 } 438 439 uint32_t 440 jz4730_cpm_get_cpu_frequency(void *cpm) 441 { 442 return static_cast<Cpm_jz4730_chip *>(cpm)->get_cpu_frequency(); 443 } 444 445 uint32_t 446 jz4730_cpm_get_hclock_frequency(void *cpm) 447 { 448 return static_cast<Cpm_jz4730_chip *>(cpm)->get_hclock_frequency(); 449 } 450 451 uint32_t 452 jz4730_cpm_get_source_frequency(void *cpm) 453 { 454 return static_cast<Cpm_jz4730_chip *>(cpm)->get_source_frequency(); 455 } 456 457 uint32_t 458 jz4730_cpm_get_pclock_frequency(void *cpm) 459 { 460 return static_cast<Cpm_jz4730_chip *>(cpm)->get_pclock_frequency(); 461 } 462 463 uint32_t 464 jz4730_cpm_get_memory_frequency(void *cpm) 465 { 466 return static_cast<Cpm_jz4730_chip *>(cpm)->get_memory_frequency(); 467 } 468 469 uint16_t 470 jz4730_cpm_get_lcd_pixel_divider(void *cpm) 471 { 472 return static_cast<Cpm_jz4730_chip *>(cpm)->get_lcd_pixel_divider(); 473 } 474 475 uint32_t 476 jz4730_cpm_get_frequency(void *cpm, enum Clock_identifiers clock) 477 { 478 return static_cast<Cpm_jz4730_chip *>(cpm)->get_frequency(clock); 479 } 480 481 void 482 jz4730_cpm_set_frequency(void *cpm, enum Clock_identifiers clock, uint32_t frequency) 483 { 484 static_cast<Cpm_jz4730_chip *>(cpm)->set_frequency(clock, frequency); 485 }