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 #pragma once 23 24 #ifdef __cplusplus 25 26 #include <l4/devices/hw_register_block.h> 27 #include <l4/devices/cpm.h> 28 #include <l4/sys/types.h> 29 #include <stdint.h> 30 31 /* Forward declaration. */ 32 33 class Clock_base; 34 35 36 37 /* Register access type. */ 38 39 class Cpm_regs 40 { 41 Hw::Register_block<32> _regs; 42 43 protected: 44 Clock_base **_clocks; 45 46 public: 47 uint32_t exclk_freq; 48 49 explicit Cpm_regs(l4_addr_t addr, Clock_base *clocks[], 50 uint32_t exclk_freq); 51 52 // Utility methods. 53 54 uint32_t get_field(uint32_t reg, uint32_t mask, uint8_t shift); 55 void set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value); 56 57 Clock_base *get_clock(int num); 58 }; 59 60 61 62 // Register field abstraction. 63 64 class Field 65 { 66 uint32_t reg; 67 uint32_t mask; 68 uint8_t bit; 69 bool defined; 70 71 public: 72 explicit Field() 73 : defined(false) 74 { 75 } 76 77 explicit Field(uint32_t reg, uint32_t mask, uint32_t bit) 78 : reg(reg), mask(mask), bit(bit), defined(true) 79 { 80 } 81 82 uint32_t get_field(Cpm_regs ®s); 83 void set_field(Cpm_regs ®s, uint32_t value); 84 bool is_defined() { return defined; } 85 86 // Undefined field object. 87 88 static Field undefined; 89 }; 90 91 92 93 // Clock sources. 94 95 class Mux 96 { 97 int _num_inputs; 98 enum Clock_identifiers *_inputs, _input; 99 100 public: 101 explicit Mux(int num_inputs, enum Clock_identifiers inputs[]) 102 : _num_inputs(num_inputs), _inputs(inputs) 103 { 104 } 105 106 explicit Mux(enum Clock_identifiers input) 107 : _num_inputs(1), _inputs(&_input) 108 { 109 _input = input; 110 } 111 112 explicit Mux() 113 : _num_inputs(0), _inputs(NULL) 114 { 115 } 116 117 int get_number() { return _num_inputs; } 118 enum Clock_identifiers get_input(int num); 119 }; 120 121 122 123 class Source 124 { 125 Mux _inputs; 126 Field _source; 127 128 public: 129 explicit Source(Mux inputs, Field source) 130 : _inputs(inputs), _source(source) 131 { 132 } 133 134 explicit Source(Mux inputs) 135 : _inputs(inputs) 136 { 137 } 138 139 explicit Source() 140 { 141 } 142 143 int get_number() { return _inputs.get_number(); } 144 enum Clock_identifiers get_input(int num) { return _inputs.get_input(num); } 145 146 // Clock source. 147 148 uint8_t get_source(Cpm_regs ®s); 149 void set_source(Cpm_regs ®s, uint8_t source); 150 151 // Clock source frequency. 152 153 uint32_t get_frequency(Cpm_regs ®s); 154 155 // Undefined source object. 156 157 static Source undefined; 158 }; 159 160 161 162 // Frequency transformation. 163 164 class Transform 165 { 166 public: 167 168 // Output frequency. 169 170 virtual uint32_t get_frequency(Cpm_regs ®s, uint32_t source_frequency) = 0; 171 }; 172 173 174 175 class Divider : public Transform 176 { 177 Field _divider; 178 179 public: 180 explicit Divider(Field divider) 181 : _divider(divider) 182 { 183 } 184 185 explicit Divider() 186 : _divider(Field::undefined) 187 { 188 } 189 190 // Clock divider. 191 192 uint32_t get_divider(Cpm_regs ®s); 193 void set_divider(Cpm_regs ®s, uint32_t division); 194 195 // Output frequency. 196 197 uint32_t get_frequency(Cpm_regs ®s, uint32_t source_frequency); 198 199 // Undefined divider. 200 201 static Divider undefined; 202 }; 203 204 205 206 class Divider_pll : public Transform 207 { 208 Field _multiplier, _input_division, _output_division0, _output_division1; 209 210 public: 211 explicit Divider_pll(Field multiplier, Field input_division, 212 Field output_division0, Field output_division1) 213 : _multiplier(multiplier), _input_division(input_division), 214 _output_division0(output_division0), _output_division1(output_division1) 215 { 216 } 217 218 // General frequency modifiers. 219 220 uint16_t get_multiplier(Cpm_regs ®s); 221 void set_multiplier(Cpm_regs ®s, uint16_t multiplier); 222 uint8_t get_input_division(Cpm_regs ®s); 223 void set_input_division(Cpm_regs ®s, uint8_t divider); 224 uint8_t get_output_division(Cpm_regs ®s); 225 void set_output_division(Cpm_regs ®s, uint8_t divider); 226 227 // Output frequency. 228 229 uint32_t get_frequency(Cpm_regs ®s, uint32_t source_frequency); 230 231 // Other operations. 232 233 void set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, 234 uint8_t in_divider, uint8_t out_divider); 235 }; 236 237 238 239 // Common clock abstraction. 240 241 class Clock_base 242 { 243 Source _source; 244 245 public: 246 explicit Clock_base(Source source) 247 : _source(source) 248 { 249 } 250 251 // Clock control. 252 253 virtual int have_clock(Cpm_regs ®s); 254 virtual void start_clock(Cpm_regs ®s); 255 virtual void stop_clock(Cpm_regs ®s); 256 257 // Clock divider. 258 259 virtual uint32_t get_divider(Cpm_regs ®s); 260 virtual void set_divider(Cpm_regs ®s, uint32_t division); 261 262 // Clock source. 263 264 virtual uint8_t get_source(Cpm_regs ®s); 265 virtual void set_source(Cpm_regs ®s, uint8_t source); 266 267 // Clock source frequency. 268 269 virtual uint32_t get_source_frequency(Cpm_regs ®s); 270 271 // Output frequency. 272 273 virtual uint32_t get_frequency(Cpm_regs ®s); 274 }; 275 276 277 278 // PLL descriptions. 279 280 class Pll : public Clock_base 281 { 282 Field _enable, _stable, _bypass; 283 Divider_pll _divider; 284 285 public: 286 explicit Pll(Source source, 287 Field enable, Field stable, Field bypass, 288 Divider_pll divider) 289 : Clock_base(source), 290 _enable(enable), _stable(stable), _bypass(bypass), 291 _divider(divider) 292 { 293 } 294 295 // PLL_specific control. 296 297 int have_pll(Cpm_regs ®s); 298 int pll_enabled(Cpm_regs ®s); 299 int pll_bypassed(Cpm_regs ®s); 300 301 // Clock control. 302 303 int have_clock(Cpm_regs ®s); 304 void start_clock(Cpm_regs ®s); 305 void stop_clock(Cpm_regs ®s); 306 307 // General frequency modifiers. 308 309 uint16_t get_multiplier(Cpm_regs ®s); 310 void set_multiplier(Cpm_regs ®s, uint16_t multiplier); 311 uint8_t get_input_division(Cpm_regs ®s); 312 void set_input_division(Cpm_regs ®s, uint8_t divider); 313 uint8_t get_output_division(Cpm_regs ®s); 314 void set_output_division(Cpm_regs ®s, uint8_t divider); 315 316 // PLL output frequency. 317 318 uint32_t get_frequency(Cpm_regs ®s); 319 320 // Other operations. 321 322 void set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, 323 uint8_t in_divider, uint8_t out_divider); 324 }; 325 326 327 328 // Clock descriptions. 329 330 class Clock : public Clock_base 331 { 332 Field _gate, _change_enable, _busy; 333 Divider _divider; 334 335 // Clock control. 336 337 void change_disable(Cpm_regs ®s); 338 void change_enable(Cpm_regs ®s); 339 void wait_busy(Cpm_regs ®s); 340 341 public: 342 explicit Clock(Source source = Source::undefined, 343 Field gate = Field::undefined, 344 Field change_enable = Field::undefined, 345 Field busy = Field::undefined, 346 Divider divider = Divider::undefined) 347 : Clock_base(source), 348 _gate(gate), _change_enable(change_enable), _busy(busy), _divider(divider) 349 { 350 } 351 352 // Clock control. 353 354 int have_clock(Cpm_regs ®s); 355 void start_clock(Cpm_regs ®s); 356 void stop_clock(Cpm_regs ®s); 357 358 // Clock divider. 359 360 uint32_t get_divider(Cpm_regs ®s); 361 void set_divider(Cpm_regs ®s, uint32_t division); 362 363 // Clock source. 364 365 void set_source(Cpm_regs ®s, uint8_t source); 366 367 // Output frequency. 368 369 uint32_t get_frequency(Cpm_regs ®s); 370 }; 371 372 #endif /* __cplusplus */