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 // Return the external clock frequency without any input clock. 121 122 if (get_number() == 0) 123 return regs.exclk_freq; 124 125 // Clocks with one source yield that input frequency. 126 127 else if (get_number() == 1) 128 return regs.get_clock(get_input(0))->get_frequency(regs); 129 130 // With multiple sources, obtain the selected source for the clock. 131 132 uint8_t source = get_source(regs); 133 enum Clock_identifiers input = get_input(source); 134 135 // Return the frequency of the source. 136 137 if (input != Clock_undefined) 138 return regs.get_clock(input)->get_frequency(regs); 139 else 140 return 0; 141 } 142 143 // Undefined source. 144 145 Source Source::undefined; 146 147 148 149 // Clock dividers. 150 151 uint32_t 152 Divider::get_divider(Cpm_regs ®s) 153 { 154 if (_divider.is_defined()) 155 return _divider.get_field(regs) + 1; 156 else 157 return 1; 158 } 159 160 void 161 Divider::set_divider(Cpm_regs ®s, uint32_t division) 162 { 163 if (_divider.is_defined()) 164 _divider.set_field(regs, division - 1); 165 } 166 167 // Output clock frequencies. 168 169 uint32_t 170 Divider::get_frequency(Cpm_regs ®s, uint32_t source_frequency) 171 { 172 return source_frequency / get_divider(regs); 173 } 174 175 // Undefined divider. 176 177 Divider Divider::undefined; 178 179 180 181 // Feedback (13-bit) multiplier. 182 183 uint16_t 184 Divider_pll::get_multiplier(Cpm_regs ®s) 185 { 186 return _multiplier.get_field(regs) + 1; 187 } 188 189 void 190 Divider_pll::set_multiplier(Cpm_regs ®s, uint16_t multiplier) 191 { 192 _multiplier.set_field(regs, multiplier - 1); 193 } 194 195 // Input (6-bit) divider. 196 197 uint8_t 198 Divider_pll::get_input_division(Cpm_regs ®s) 199 { 200 return _input_division.get_field(regs) + 1; 201 } 202 203 void 204 Divider_pll::set_input_division(Cpm_regs ®s, uint8_t divider) 205 { 206 _input_division.set_field(regs, divider - 1); 207 } 208 209 // Output (dual 3-bit) dividers. 210 211 uint8_t 212 Divider_pll::get_output_division(Cpm_regs ®s) 213 { 214 uint8_t d0 = _output_division0.get_field(regs); 215 uint8_t d1 = _output_division1.get_field(regs); 216 217 return d0 * d1; 218 } 219 220 void 221 Divider_pll::set_output_division(Cpm_regs ®s, uint8_t divider) 222 { 223 // Assert 1 as a minimum. 224 // Divider 0 must be less than or equal to divider 1. 225 226 uint8_t d0 = (uint8_t) floor(sqrt(divider ? divider : 1)); 227 uint8_t d1 = divider / d0; 228 229 _output_division0.set_field(regs, d0); 230 _output_division1.set_field(regs, d1); 231 } 232 233 uint32_t 234 Divider_pll::get_frequency(Cpm_regs ®s, uint32_t source_frequency) 235 { 236 return (source_frequency * get_multiplier(regs)) / 237 (get_input_division(regs) * get_output_division(regs)); 238 } 239 240 void 241 Divider_pll::set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, 242 uint8_t in_divider, uint8_t out_divider) 243 { 244 set_multiplier(regs, multiplier); 245 set_input_division(regs, in_divider); 246 set_output_division(regs, out_divider); 247 } 248 249 250 251 // Clock control. 252 253 int 254 Clock_base::have_clock(Cpm_regs ®s) 255 { 256 (void) regs; 257 return true; 258 } 259 260 void 261 Clock_base::start_clock(Cpm_regs ®s) 262 { 263 (void) regs; 264 } 265 266 void 267 Clock_base::stop_clock(Cpm_regs ®s) 268 { 269 (void) regs; 270 } 271 272 // Default divider. 273 274 uint32_t 275 Clock_base::get_divider(Cpm_regs ®s) 276 { 277 (void) regs; 278 return 1; 279 } 280 281 void 282 Clock_base::set_divider(Cpm_regs ®s, uint32_t division) 283 { 284 (void) regs; 285 (void) division; 286 } 287 288 // Clock sources. 289 290 uint8_t 291 Clock_base::get_source(Cpm_regs ®s) 292 { 293 return _source.get_source(regs); 294 } 295 296 void 297 Clock_base::set_source(Cpm_regs ®s, uint8_t source) 298 { 299 _source.set_source(regs, source); 300 } 301 302 // Clock source frequencies. 303 304 uint32_t 305 Clock_base::get_source_frequency(Cpm_regs ®s) 306 { 307 return _source.get_frequency(regs); 308 } 309 310 // Output clock frequencies. 311 312 uint32_t 313 Clock_base::get_frequency(Cpm_regs ®s) 314 { 315 return get_source_frequency(regs); 316 } 317 318 319 320 // PLL-specific control. 321 322 int 323 Pll::have_pll(Cpm_regs ®s) 324 { 325 return _stable.get_field(regs); 326 } 327 328 int 329 Pll::pll_enabled(Cpm_regs ®s) 330 { 331 return _enable.get_field(regs); 332 } 333 334 int 335 Pll::pll_bypassed(Cpm_regs ®s) 336 { 337 return _bypass.get_field(regs); 338 } 339 340 // Clock control. 341 342 int 343 Pll::have_clock(Cpm_regs ®s) 344 { 345 return have_pll(regs) && pll_enabled(regs); 346 } 347 348 void 349 Pll::start_clock(Cpm_regs ®s) 350 { 351 _enable.set_field(regs, 1); 352 while (!have_pll(regs)); 353 } 354 355 void 356 Pll::stop_clock(Cpm_regs ®s) 357 { 358 _enable.set_field(regs, 0); 359 while (have_pll(regs)); 360 } 361 362 // Feedback (13-bit) multiplier. 363 364 uint16_t 365 Pll::get_multiplier(Cpm_regs ®s) 366 { 367 return _divider.get_multiplier(regs); 368 } 369 370 void 371 Pll::set_multiplier(Cpm_regs ®s, uint16_t multiplier) 372 { 373 _divider.set_multiplier(regs, multiplier); 374 } 375 376 // Input (6-bit) divider. 377 378 uint8_t 379 Pll::get_input_division(Cpm_regs ®s) 380 { 381 return _divider.get_input_division(regs); 382 } 383 384 void 385 Pll::set_input_division(Cpm_regs ®s, uint8_t divider) 386 { 387 _divider.set_input_division(regs, divider); 388 } 389 390 // Output (dual 3-bit) dividers. 391 392 uint8_t 393 Pll::get_output_division(Cpm_regs ®s) 394 { 395 return _divider.get_output_division(regs); 396 } 397 398 void 399 Pll::set_output_division(Cpm_regs ®s, uint8_t divider) 400 { 401 _divider.set_output_division(regs, divider); 402 } 403 404 uint32_t 405 Pll::get_frequency(Cpm_regs ®s) 406 { 407 // Test for PLL enable and not PLL bypass. 408 409 if (pll_enabled(regs)) 410 { 411 if (!pll_bypassed(regs)) 412 return _divider.get_frequency(regs, get_source_frequency(regs)); 413 else 414 return get_source_frequency(regs); 415 } 416 else 417 return 0; 418 } 419 420 void 421 Pll::set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, 422 uint8_t in_divider, uint8_t out_divider) 423 { 424 set_pll_parameters(regs, multiplier, in_divider, out_divider); 425 426 if (pll_enabled(regs) && !pll_bypassed(regs)) 427 while (!have_pll(regs)); 428 } 429 430 431 432 // Clock control. 433 434 void 435 Clock::change_disable(Cpm_regs ®s) 436 { 437 if (_change_enable.is_defined()) 438 _change_enable.set_field(regs, 0); 439 } 440 441 void 442 Clock::change_enable(Cpm_regs ®s) 443 { 444 if (_change_enable.is_defined()) 445 _change_enable.set_field(regs, 1); 446 } 447 448 int 449 Clock::have_clock(Cpm_regs ®s) 450 { 451 if (_gate.is_defined()) 452 return !_gate.get_field(regs); 453 else 454 return true; 455 } 456 457 void 458 Clock::start_clock(Cpm_regs ®s) 459 { 460 if (_gate.is_defined()) 461 _gate.set_field(regs, 0); 462 } 463 464 void 465 Clock::stop_clock(Cpm_regs ®s) 466 { 467 if (_gate.is_defined()) 468 _gate.set_field(regs, 1); 469 } 470 471 void 472 Clock::wait_busy(Cpm_regs ®s) 473 { 474 if (_busy.is_defined()) 475 while (_busy.get_field(regs)); 476 } 477 478 // Clock dividers. 479 480 uint32_t 481 Clock::get_divider(Cpm_regs ®s) 482 { 483 return _divider.get_divider(regs); 484 } 485 486 void 487 Clock::set_divider(Cpm_regs ®s, uint32_t division) 488 { 489 change_enable(regs); 490 _divider.set_divider(regs, division); 491 wait_busy(regs); 492 change_disable(regs); 493 } 494 495 void 496 Clock::set_source(Cpm_regs ®s, uint8_t source) 497 { 498 change_enable(regs); 499 Clock_base::set_source(regs, source); 500 wait_busy(regs); 501 change_disable(regs); 502 } 503 504 // Output clock frequencies. 505 506 uint32_t 507 Clock::get_frequency(Cpm_regs ®s) 508 { 509 return _divider.get_frequency(regs, get_source_frequency(regs)); 510 }