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 * (c) 2017, 2018 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_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_lcd = 7, // LCD 61 Clock_gate_timer = 3, // OST 62 }; 63 64 enum Lcd_divider_bits : unsigned 65 { 66 Lcd_divider_value = 0, // PIXFR (in CFCR2) 67 }; 68 69 70 71 // If implemented as a Hw::Device, various properties would be 72 // initialised in the constructor and obtained from the device tree 73 // definitions. 74 75 Cpm_jz4730_chip::Cpm_jz4730_chip(l4_addr_t addr, uint32_t exclk_freq) 76 : _exclk_freq(exclk_freq) 77 { 78 _regs = new Hw::Mmio_register_block<32>(addr); 79 80 // add_cid("cpm"); 81 // add_cid("cpm-jz4730"); 82 // register_property("exclk_freq", &_exclk_freq); 83 } 84 85 // Clock/timer control. 86 87 int 88 Cpm_jz4730_chip::have_clock() 89 { 90 return !(_regs[Clock_gate] & (1 << Clock_gate_timer)); 91 } 92 93 void 94 Cpm_jz4730_chip::start_clock() 95 { 96 _regs[Clock_gate] = _regs[Clock_gate] & ~(1 << Clock_gate_timer); 97 } 98 99 // PLL control. 100 101 // Return whether the PLL is stable. 102 103 int 104 Cpm_jz4730_chip::have_pll() 105 { 106 return _regs[Pll_control] & (1 << Pll_stable); 107 } 108 109 int 110 Cpm_jz4730_chip::pll_enabled() 111 { 112 return _regs[Pll_control] & (1 << Pll_enabled); 113 } 114 115 int 116 Cpm_jz4730_chip::pll_bypassed() 117 { 118 return _regs[Pll_control] & (1 << Pll_bypassed); 119 } 120 121 // Feedback (9-bit) multiplier. 122 123 uint16_t 124 Cpm_jz4730_chip::get_multiplier() 125 { 126 return ((_regs[Pll_control] & (0x1ff << Pll_multiplier)) >> Pll_multiplier) + 2; 127 } 128 129 // Input (5-bit) divider. 130 131 uint8_t 132 Cpm_jz4730_chip::get_input_division() 133 { 134 return ((_regs[Pll_control] & (0x1f << Pll_input_division)) >> Pll_input_division) + 2; 135 } 136 137 // Output divider. 138 139 static uint8_t od[4] = {1, 2, 2, 4}; 140 141 uint8_t 142 Cpm_jz4730_chip::get_output_division() 143 { 144 return od[(_regs[Pll_control] & (0x03 << Pll_output_division)) >> Pll_output_division]; 145 } 146 147 // General clock divider. 148 149 static uint8_t cd[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; 150 151 uint8_t 152 Cpm_jz4730_chip::_get_divider(uint32_t reg, uint32_t mask, uint8_t shift) 153 { 154 uint8_t d = (_regs[reg] & mask) >> shift; 155 return (d < 10) ? cd[d] : 1; 156 } 157 158 // CPU clock (CCLK) divider. 159 160 uint8_t 161 Cpm_jz4730_chip::get_cpu_divider() 162 { 163 return _get_divider(Clock_control, 0xf << Clock_cpu_divider, Clock_cpu_divider); 164 } 165 166 // Fast peripheral clock (HCLK) divider. 167 168 uint8_t 169 Cpm_jz4730_chip::get_hclock_divider() 170 { 171 return _get_divider(Clock_control, 0xf << Clock_hclock_divider, Clock_hclock_divider); 172 } 173 174 // Slow peripheral clock (PCLK) divider. 175 176 uint8_t 177 Cpm_jz4730_chip::get_pclock_divider() 178 { 179 return _get_divider(Clock_control, 0xf << Clock_pclock_divider, Clock_pclock_divider); 180 } 181 182 // Memory clock (MCLK) divider. 183 184 uint8_t 185 Cpm_jz4730_chip::get_memory_divider() 186 { 187 return _get_divider(Clock_control, 0xf << Clock_memory_divider, Clock_memory_divider); 188 } 189 190 // Clock source divider for MSC, I2S, LCD and USB. 191 192 uint8_t 193 Cpm_jz4730_chip::get_source_divider() 194 { 195 return 1; 196 } 197 198 // LCD device clock divider. 199 200 void 201 Cpm_jz4730_chip::set_lcd_device_divider(uint8_t division) 202 { 203 if (division == 0) 204 division = 1; 205 206 // NOTE: The vendor code (clock.c) bounds the divider at 16, but maybe this is 207 // NOTE: confused with the width of the bitfield which actually contains an 208 // NOTE: index for the clock divider value array (cd). 209 210 else if (division > 16) 211 division = 16; 212 213 // Obtain the divider or closest higher divider. 214 215 int i; 216 217 for (i = 0; i < 10; i++) 218 if (cd[i] >= division) break; 219 220 _regs[Clock_control] = (_regs[Clock_control] & ~(0xf << Clock_lcd_divider)) | 221 (cd[i] << Clock_lcd_divider); 222 } 223 224 // LCD pixel clock divider. 225 226 uint16_t 227 Cpm_jz4730_chip::get_lcd_pixel_divider() 228 { 229 return (_regs[Lcd_divider] >> Lcd_divider_value) + 1; 230 } 231 232 void 233 Cpm_jz4730_chip::set_lcd_pixel_divider(uint16_t division) 234 { 235 if (division == 0) 236 division = 1; 237 else if (division > 512) 238 division = 512; 239 240 _regs[Lcd_divider] = (_regs[Lcd_divider] & ~(0x1ff << Lcd_divider_value)) | 241 ((division - 1) << Lcd_divider_value); 242 } 243 244 245 246 uint32_t 247 Cpm_jz4730_chip::get_lcd_pixel_frequency() 248 { 249 return get_output_frequency() / get_lcd_pixel_divider(); 250 } 251 252 // Set the device and pixel frequencies, indicating the latter and 253 // providing the device:pixel frequency ratio. 254 255 void 256 Cpm_jz4730_chip::set_lcd_frequencies(uint32_t pclk, uint8_t ratio) 257 { 258 uint32_t out = get_output_frequency(), 259 lcd = pclk * ratio; 260 261 set_lcd_pixel_divider(out / pclk); 262 263 // Limit the device frequency to 150MHz. 264 265 if (lcd > 150000000) lcd = 150000000; 266 267 set_lcd_device_divider(out / lcd); 268 } 269 270 271 272 // LCD clock control. 273 274 void 275 Cpm_jz4730_chip::start_lcd() 276 { 277 _regs[Clock_gate] = _regs[Clock_gate] & ~(1 << Clock_gate_lcd); 278 } 279 280 void 281 Cpm_jz4730_chip::stop_lcd() 282 { 283 _regs[Clock_gate] = _regs[Clock_gate] | (1 << Clock_gate_lcd); 284 } 285 286 287 288 uint32_t 289 Cpm_jz4730_chip::get_pll_frequency() 290 { 291 // Test for PLL enable and not PLL bypass. 292 293 if (pll_enabled() && !pll_bypassed()) 294 return (_exclk_freq * get_multiplier()) / 295 (get_input_division() * get_output_division()); 296 else 297 return _exclk_freq; 298 } 299 300 // Clock frequency for MSC, I2S, LCD and USB. 301 302 uint32_t 303 Cpm_jz4730_chip::get_output_frequency() 304 { 305 return get_pll_frequency() / get_source_divider(); 306 } 307 308 void 309 Cpm_jz4730_chip::update_output_frequency() 310 { 311 _regs[Clock_control] = _regs[Clock_control] | (1 << Clock_enable); 312 } 313 314 // Clock frequency for the CPU. 315 316 uint32_t Cpm_jz4730_chip::get_cpu_frequency() 317 { 318 if (pll_enabled()) 319 return get_pll_frequency() / get_cpu_divider(); 320 else 321 return _exclk_freq; 322 } 323 324 // Clock frequency for fast peripherals. 325 326 uint32_t 327 Cpm_jz4730_chip::get_hclock_frequency() 328 { 329 if (pll_enabled()) 330 return get_pll_frequency() / get_hclock_divider(); 331 else 332 return _exclk_freq; 333 } 334 335 // Clock frequency for slow peripherals. 336 337 uint32_t 338 Cpm_jz4730_chip::get_pclock_frequency() 339 { 340 if (pll_enabled()) 341 return get_pll_frequency() / get_pclock_divider(); 342 else 343 return _exclk_freq; 344 } 345 346 // Clock frequency for the memory. 347 348 uint32_t 349 Cpm_jz4730_chip::get_memory_frequency() 350 { 351 if (pll_enabled()) 352 return get_pll_frequency() / get_memory_divider(); 353 else 354 return _exclk_freq; 355 } 356 357 358 359 // C language interface functions. 360 361 void 362 *jz4730_cpm_init(l4_addr_t cpm_base) 363 { 364 /* Initialise the clock and power management peripheral with the 365 register memory region and a 3.6864 MHz EXCLK frequency. */ 366 367 return (void *) new Cpm_jz4730_chip(cpm_base, 3686400); 368 } 369 370 int 371 jz4730_cpm_have_clock(void *cpm) 372 { 373 return static_cast<Cpm_jz4730_chip *>(cpm)->have_clock(); 374 } 375 376 void 377 jz4730_cpm_start_clock(void *cpm) 378 { 379 static_cast<Cpm_jz4730_chip *>(cpm)->start_clock(); 380 } 381 382 void 383 jz4730_cpm_start_lcd(void *cpm) 384 { 385 static_cast<Cpm_jz4730_chip *>(cpm)->start_lcd(); 386 } 387 388 void 389 jz4730_cpm_stop_lcd(void *cpm) 390 { 391 static_cast<Cpm_jz4730_chip *>(cpm)->stop_lcd(); 392 } 393 394 uint32_t 395 jz4730_cpm_get_cpu_frequency(void *cpm) 396 { 397 return static_cast<Cpm_jz4730_chip *>(cpm)->get_cpu_frequency(); 398 } 399 400 uint32_t 401 jz4730_cpm_get_hclock_frequency(void *cpm) 402 { 403 return static_cast<Cpm_jz4730_chip *>(cpm)->get_hclock_frequency(); 404 } 405 406 uint32_t 407 jz4730_cpm_get_output_frequency(void *cpm) 408 { 409 return static_cast<Cpm_jz4730_chip *>(cpm)->get_output_frequency(); 410 } 411 412 uint32_t 413 jz4730_cpm_get_pclock_frequency(void *cpm) 414 { 415 return static_cast<Cpm_jz4730_chip *>(cpm)->get_pclock_frequency(); 416 } 417 418 uint32_t 419 jz4730_cpm_get_memory_frequency(void *cpm) 420 { 421 return static_cast<Cpm_jz4730_chip *>(cpm)->get_memory_frequency(); 422 } 423 424 uint16_t 425 jz4730_cpm_get_lcd_pixel_divider(void *cpm) 426 { 427 return static_cast<Cpm_jz4730_chip *>(cpm)->get_lcd_pixel_divider(); 428 } 429 430 uint32_t 431 jz4730_cpm_get_lcd_pixel_frequency(void *cpm) 432 { 433 return static_cast<Cpm_jz4730_chip *>(cpm)->get_lcd_pixel_frequency(); 434 } 435 436 void 437 jz4730_cpm_set_lcd_frequencies(void *cpm, uint32_t pclk, uint8_t ratio) 438 { 439 static_cast<Cpm_jz4730_chip *>(cpm)->set_lcd_frequencies(pclk, ratio); 440 } 441 442 void 443 jz4730_cpm_update_output_frequency(void *cpm) 444 { 445 static_cast<Cpm_jz4730_chip *>(cpm)->update_output_frequency(); 446 }