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 control. 150 151 int 152 Clock_base::have_clock(Cpm_regs ®s) 153 { 154 (void) regs; 155 return true; 156 } 157 158 void 159 Clock_base::start_clock(Cpm_regs ®s) 160 { 161 (void) regs; 162 } 163 164 void 165 Clock_base::stop_clock(Cpm_regs ®s) 166 { 167 (void) regs; 168 } 169 170 // Default divider. 171 172 uint32_t 173 Clock_base::get_divider(Cpm_regs ®s) 174 { 175 (void) regs; 176 return 1; 177 } 178 179 void 180 Clock_base::set_divider(Cpm_regs ®s, uint32_t division) 181 { 182 (void) regs; 183 (void) division; 184 } 185 186 // Clock sources. 187 188 uint8_t 189 Clock_base::get_source(Cpm_regs ®s) 190 { 191 return _source.get_source(regs); 192 } 193 194 void 195 Clock_base::set_source(Cpm_regs ®s, uint8_t source) 196 { 197 _source.set_source(regs, source); 198 } 199 200 // Clock source frequencies. 201 202 uint32_t 203 Clock_base::get_source_frequency(Cpm_regs ®s) 204 { 205 return _source.get_frequency(regs); 206 } 207 208 // Output clock frequencies. 209 210 uint32_t 211 Clock_base::get_frequency(Cpm_regs ®s) 212 { 213 return get_source_frequency(regs) / get_divider(regs); 214 } 215 216 217 218 // PLL-specific control. 219 220 int 221 Pll::have_pll(Cpm_regs ®s) 222 { 223 return _stable.get_field(regs); 224 } 225 226 int 227 Pll::pll_enabled(Cpm_regs ®s) 228 { 229 return _enable.get_field(regs); 230 } 231 232 int 233 Pll::pll_bypassed(Cpm_regs ®s) 234 { 235 return _bypass.get_field(regs); 236 } 237 238 // Clock control. 239 240 int 241 Pll::have_clock(Cpm_regs ®s) 242 { 243 return have_pll(regs) && pll_enabled(regs); 244 } 245 246 void 247 Pll::start_clock(Cpm_regs ®s) 248 { 249 _enable.set_field(regs, 1); 250 while (!have_pll(regs)); 251 } 252 253 void 254 Pll::stop_clock(Cpm_regs ®s) 255 { 256 _enable.set_field(regs, 0); 257 while (have_pll(regs)); 258 } 259 260 // Feedback (13-bit) multiplier. 261 262 uint16_t 263 Pll::get_multiplier(Cpm_regs ®s) 264 { 265 return _multiplier.get_field(regs) + 1; 266 } 267 268 void 269 Pll::set_multiplier(Cpm_regs ®s, uint16_t multiplier) 270 { 271 _multiplier.set_field(regs, multiplier - 1); 272 } 273 274 // Input (6-bit) divider. 275 276 uint8_t 277 Pll::get_input_division(Cpm_regs ®s) 278 { 279 return _input_division.get_field(regs) + 1; 280 } 281 282 void 283 Pll::set_input_division(Cpm_regs ®s, uint8_t divider) 284 { 285 _input_division.set_field(regs, divider - 1); 286 } 287 288 // Output (dual 3-bit) dividers. 289 290 uint8_t 291 Pll::get_output_division(Cpm_regs ®s) 292 { 293 uint8_t d0 = _output_division0.get_field(regs); 294 uint8_t d1 = _output_division1.get_field(regs); 295 296 return d0 * d1; 297 } 298 299 void 300 Pll::set_output_division(Cpm_regs ®s, uint8_t divider) 301 { 302 // Assert 1 as a minimum. 303 // Divider 0 must be less than or equal to divider 1. 304 305 uint8_t d0 = (uint8_t) floor(sqrt(divider ? divider : 1)); 306 uint8_t d1 = divider / d0; 307 308 _output_division0.set_field(regs, d0); 309 _output_division1.set_field(regs, d1); 310 } 311 312 uint32_t 313 Pll::get_frequency(Cpm_regs ®s) 314 { 315 // Test for PLL enable and not PLL bypass. 316 317 if (pll_enabled(regs)) 318 { 319 if (!pll_bypassed(regs)) 320 return (get_source_frequency(regs) * get_multiplier(regs)) / 321 (get_input_division(regs) * get_output_division(regs)); 322 else 323 return get_source_frequency(regs); 324 } 325 else 326 return 0; 327 } 328 329 void 330 Pll::set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) 331 { 332 set_multiplier(regs, multiplier); 333 set_input_division(regs, in_divider); 334 set_output_division(regs, out_divider); 335 336 if (pll_enabled(regs) && !pll_bypassed(regs)) 337 while (!have_pll(regs)); 338 } 339 340 341 342 // Clock control. 343 344 void 345 Clock::change_disable(Cpm_regs ®s) 346 { 347 if (_change_enable.is_defined()) 348 _change_enable.set_field(regs, 0); 349 } 350 351 void 352 Clock::change_enable(Cpm_regs ®s) 353 { 354 if (_change_enable.is_defined()) 355 _change_enable.set_field(regs, 1); 356 } 357 358 int 359 Clock::have_clock(Cpm_regs ®s) 360 { 361 if (_gate.is_defined()) 362 return !_gate.get_field(regs); 363 else 364 return true; 365 } 366 367 void 368 Clock::start_clock(Cpm_regs ®s) 369 { 370 if (_gate.is_defined()) 371 _gate.set_field(regs, 0); 372 } 373 374 void 375 Clock::stop_clock(Cpm_regs ®s) 376 { 377 if (_gate.is_defined()) 378 _gate.set_field(regs, 1); 379 } 380 381 void 382 Clock::wait_busy(Cpm_regs ®s) 383 { 384 if (_busy.is_defined()) 385 while (_busy.get_field(regs)); 386 } 387 388 389 390 // Clock dividers. 391 392 uint32_t 393 Clock::get_divider(Cpm_regs ®s) 394 { 395 if (_divider.is_defined()) 396 return _divider.get_field(regs) + 1; 397 else 398 return 1; 399 } 400 401 void 402 Clock::set_divider(Cpm_regs ®s, uint32_t division) 403 { 404 if (!_divider.is_defined()) 405 return; 406 407 change_enable(regs); 408 _divider.set_field(regs, division - 1); 409 wait_busy(regs); 410 change_disable(regs); 411 } 412 413 void 414 Clock::set_source(Cpm_regs ®s, uint8_t source) 415 { 416 change_enable(regs); 417 Clock_base::set_source(regs, source); 418 wait_busy(regs); 419 change_disable(regs); 420 }