1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/cpm/src/common.cc Sat Sep 16 16:53:06 2023 +0200
1.3 @@ -0,0 +1,420 @@
1.4 +/*
1.5 + * Common clock functionality.
1.6 + *
1.7 + * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <l4/devices/hw_mmio_register_block.h>
1.26 +
1.27 +#include "cpm-common.h"
1.28 +#include <math.h>
1.29 +
1.30 +
1.31 +
1.32 +// Register access.
1.33 +
1.34 +Cpm_regs::Cpm_regs(l4_addr_t addr, Clock_base *clocks[],
1.35 + uint32_t exclk_freq)
1.36 +: _clocks(clocks), exclk_freq(exclk_freq)
1.37 +{
1.38 + _regs = new Hw::Mmio_register_block<32>(addr);
1.39 +}
1.40 +
1.41 +// Utility methods.
1.42 +
1.43 +uint32_t
1.44 +Cpm_regs::get_field(uint32_t reg, uint32_t mask, uint8_t shift)
1.45 +{
1.46 + return (_regs[reg] & (mask << shift)) >> shift;
1.47 +}
1.48 +
1.49 +void
1.50 +Cpm_regs::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value)
1.51 +{
1.52 + _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift);
1.53 +}
1.54 +
1.55 +Clock_base *
1.56 +Cpm_regs::get_clock(int num)
1.57 +{
1.58 + return _clocks[num];
1.59 +}
1.60 +
1.61 +
1.62 +
1.63 +// Field methods.
1.64 +
1.65 +uint32_t
1.66 +Field::get_field(Cpm_regs ®s)
1.67 +{
1.68 + if (defined)
1.69 + return regs.get_field(reg, mask, bit);
1.70 + else
1.71 + return 0;
1.72 +}
1.73 +
1.74 +void
1.75 +Field::set_field(Cpm_regs ®s, uint32_t value)
1.76 +{
1.77 + if (defined)
1.78 + regs.set_field(reg, mask, bit, value);
1.79 +}
1.80 +
1.81 +// Undefined field.
1.82 +
1.83 +Field Field::undefined;
1.84 +
1.85 +
1.86 +
1.87 +// Clock sources.
1.88 +
1.89 +enum Clock_identifiers
1.90 +Mux::get_input(int num)
1.91 +{
1.92 + if (num < _num_inputs)
1.93 + return _inputs[num];
1.94 + else
1.95 + return Clock_undefined;
1.96 +}
1.97 +
1.98 +// Clock sources.
1.99 +
1.100 +uint8_t
1.101 +Source::get_source(Cpm_regs ®s)
1.102 +{
1.103 + if (_source.is_defined())
1.104 + return _source.get_field(regs);
1.105 + else
1.106 + return 0;
1.107 +}
1.108 +
1.109 +void
1.110 +Source::set_source(Cpm_regs ®s, uint8_t source)
1.111 +{
1.112 + if (!_source.is_defined())
1.113 + return;
1.114 +
1.115 + _source.set_field(regs, source);
1.116 +}
1.117 +
1.118 +// Clock source frequencies.
1.119 +
1.120 +uint32_t
1.121 +Source::get_frequency(Cpm_regs ®s)
1.122 +{
1.123 + // Return the external clock frequency without any input clock.
1.124 +
1.125 + if (get_number() == 0)
1.126 + return regs.exclk_freq;
1.127 +
1.128 + // Clocks with one source yield that input frequency.
1.129 +
1.130 + else if (get_number() == 1)
1.131 + return regs.get_clock(get_input(0))->get_frequency(regs);
1.132 +
1.133 + // With multiple sources, obtain the selected source for the clock.
1.134 +
1.135 + uint8_t source = get_source(regs);
1.136 + enum Clock_identifiers input = get_input(source);
1.137 +
1.138 + // Return the frequency of the source.
1.139 +
1.140 + if (input != Clock_undefined)
1.141 + return regs.get_clock(input)->get_frequency(regs);
1.142 + else
1.143 + return 0;
1.144 +}
1.145 +
1.146 +// Undefined source.
1.147 +
1.148 +Source Source::undefined;
1.149 +
1.150 +
1.151 +
1.152 +// Clock control.
1.153 +
1.154 +int
1.155 +Clock_base::have_clock(Cpm_regs ®s)
1.156 +{
1.157 + (void) regs;
1.158 + return true;
1.159 +}
1.160 +
1.161 +void
1.162 +Clock_base::start_clock(Cpm_regs ®s)
1.163 +{
1.164 + (void) regs;
1.165 +}
1.166 +
1.167 +void
1.168 +Clock_base::stop_clock(Cpm_regs ®s)
1.169 +{
1.170 + (void) regs;
1.171 +}
1.172 +
1.173 +// Default divider.
1.174 +
1.175 +uint32_t
1.176 +Clock_base::get_divider(Cpm_regs ®s)
1.177 +{
1.178 + (void) regs;
1.179 + return 1;
1.180 +}
1.181 +
1.182 +void
1.183 +Clock_base::set_divider(Cpm_regs ®s, uint32_t division)
1.184 +{
1.185 + (void) regs;
1.186 + (void) division;
1.187 +}
1.188 +
1.189 +// Clock sources.
1.190 +
1.191 +uint8_t
1.192 +Clock_base::get_source(Cpm_regs ®s)
1.193 +{
1.194 + return _source.get_source(regs);
1.195 +}
1.196 +
1.197 +void
1.198 +Clock_base::set_source(Cpm_regs ®s, uint8_t source)
1.199 +{
1.200 + _source.set_source(regs, source);
1.201 +}
1.202 +
1.203 +// Clock source frequencies.
1.204 +
1.205 +uint32_t
1.206 +Clock_base::get_source_frequency(Cpm_regs ®s)
1.207 +{
1.208 + return _source.get_frequency(regs);
1.209 +}
1.210 +
1.211 +// Output clock frequencies.
1.212 +
1.213 +uint32_t
1.214 +Clock_base::get_frequency(Cpm_regs ®s)
1.215 +{
1.216 + return get_source_frequency(regs) / get_divider(regs);
1.217 +}
1.218 +
1.219 +
1.220 +
1.221 +// PLL-specific control.
1.222 +
1.223 +int
1.224 +Pll::have_pll(Cpm_regs ®s)
1.225 +{
1.226 + return _stable.get_field(regs);
1.227 +}
1.228 +
1.229 +int
1.230 +Pll::pll_enabled(Cpm_regs ®s)
1.231 +{
1.232 + return _enable.get_field(regs);
1.233 +}
1.234 +
1.235 +int
1.236 +Pll::pll_bypassed(Cpm_regs ®s)
1.237 +{
1.238 + return _bypass.get_field(regs);
1.239 +}
1.240 +
1.241 +// Clock control.
1.242 +
1.243 +int
1.244 +Pll::have_clock(Cpm_regs ®s)
1.245 +{
1.246 + return have_pll(regs) && pll_enabled(regs);
1.247 +}
1.248 +
1.249 +void
1.250 +Pll::start_clock(Cpm_regs ®s)
1.251 +{
1.252 + _enable.set_field(regs, 1);
1.253 + while (!have_pll(regs));
1.254 +}
1.255 +
1.256 +void
1.257 +Pll::stop_clock(Cpm_regs ®s)
1.258 +{
1.259 + _enable.set_field(regs, 0);
1.260 + while (have_pll(regs));
1.261 +}
1.262 +
1.263 +// Feedback (13-bit) multiplier.
1.264 +
1.265 +uint16_t
1.266 +Pll::get_multiplier(Cpm_regs ®s)
1.267 +{
1.268 + return _multiplier.get_field(regs) + 1;
1.269 +}
1.270 +
1.271 +void
1.272 +Pll::set_multiplier(Cpm_regs ®s, uint16_t multiplier)
1.273 +{
1.274 + _multiplier.set_field(regs, multiplier - 1);
1.275 +}
1.276 +
1.277 +// Input (6-bit) divider.
1.278 +
1.279 +uint8_t
1.280 +Pll::get_input_division(Cpm_regs ®s)
1.281 +{
1.282 + return _input_division.get_field(regs) + 1;
1.283 +}
1.284 +
1.285 +void
1.286 +Pll::set_input_division(Cpm_regs ®s, uint8_t divider)
1.287 +{
1.288 + _input_division.set_field(regs, divider - 1);
1.289 +}
1.290 +
1.291 +// Output (dual 3-bit) dividers.
1.292 +
1.293 +uint8_t
1.294 +Pll::get_output_division(Cpm_regs ®s)
1.295 +{
1.296 + uint8_t d0 = _output_division0.get_field(regs);
1.297 + uint8_t d1 = _output_division1.get_field(regs);
1.298 +
1.299 + return d0 * d1;
1.300 +}
1.301 +
1.302 +void
1.303 +Pll::set_output_division(Cpm_regs ®s, uint8_t divider)
1.304 +{
1.305 + // Assert 1 as a minimum.
1.306 + // Divider 0 must be less than or equal to divider 1.
1.307 +
1.308 + uint8_t d0 = (uint8_t) floor(sqrt(divider ? divider : 1));
1.309 + uint8_t d1 = divider / d0;
1.310 +
1.311 + _output_division0.set_field(regs, d0);
1.312 + _output_division1.set_field(regs, d1);
1.313 +}
1.314 +
1.315 +uint32_t
1.316 +Pll::get_frequency(Cpm_regs ®s)
1.317 +{
1.318 + // Test for PLL enable and not PLL bypass.
1.319 +
1.320 + if (pll_enabled(regs))
1.321 + {
1.322 + if (!pll_bypassed(regs))
1.323 + return (get_source_frequency(regs) * get_multiplier(regs)) /
1.324 + (get_input_division(regs) * get_output_division(regs));
1.325 + else
1.326 + return get_source_frequency(regs);
1.327 + }
1.328 + else
1.329 + return 0;
1.330 +}
1.331 +
1.332 +void
1.333 +Pll::set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider)
1.334 +{
1.335 + set_multiplier(regs, multiplier);
1.336 + set_input_division(regs, in_divider);
1.337 + set_output_division(regs, out_divider);
1.338 +
1.339 + if (pll_enabled(regs) && !pll_bypassed(regs))
1.340 + while (!have_pll(regs));
1.341 +}
1.342 +
1.343 +
1.344 +
1.345 +// Clock control.
1.346 +
1.347 +void
1.348 +Clock::change_disable(Cpm_regs ®s)
1.349 +{
1.350 + if (_change_enable.is_defined())
1.351 + _change_enable.set_field(regs, 0);
1.352 +}
1.353 +
1.354 +void
1.355 +Clock::change_enable(Cpm_regs ®s)
1.356 +{
1.357 + if (_change_enable.is_defined())
1.358 + _change_enable.set_field(regs, 1);
1.359 +}
1.360 +
1.361 +int
1.362 +Clock::have_clock(Cpm_regs ®s)
1.363 +{
1.364 + if (_gate.is_defined())
1.365 + return !_gate.get_field(regs);
1.366 + else
1.367 + return true;
1.368 +}
1.369 +
1.370 +void
1.371 +Clock::start_clock(Cpm_regs ®s)
1.372 +{
1.373 + if (_gate.is_defined())
1.374 + _gate.set_field(regs, 0);
1.375 +}
1.376 +
1.377 +void
1.378 +Clock::stop_clock(Cpm_regs ®s)
1.379 +{
1.380 + if (_gate.is_defined())
1.381 + _gate.set_field(regs, 1);
1.382 +}
1.383 +
1.384 +void
1.385 +Clock::wait_busy(Cpm_regs ®s)
1.386 +{
1.387 + if (_busy.is_defined())
1.388 + while (_busy.get_field(regs));
1.389 +}
1.390 +
1.391 +
1.392 +
1.393 +// Clock dividers.
1.394 +
1.395 +uint32_t
1.396 +Clock::get_divider(Cpm_regs ®s)
1.397 +{
1.398 + if (_divider.is_defined())
1.399 + return _divider.get_field(regs) + 1;
1.400 + else
1.401 + return 1;
1.402 +}
1.403 +
1.404 +void
1.405 +Clock::set_divider(Cpm_regs ®s, uint32_t division)
1.406 +{
1.407 + if (!_divider.is_defined())
1.408 + return;
1.409 +
1.410 + change_enable(regs);
1.411 + _divider.set_field(regs, division - 1);
1.412 + wait_busy(regs);
1.413 + change_disable(regs);
1.414 +}
1.415 +
1.416 +void
1.417 +Clock::set_source(Cpm_regs ®s, uint8_t source)
1.418 +{
1.419 + change_enable(regs);
1.420 + Clock_base::set_source(regs, source);
1.421 + wait_busy(regs);
1.422 + change_disable(regs);
1.423 +}