# HG changeset patch # User Paul Boddie # Date 1694875986 -7200 # Node ID fd56764dd69659409bc7643ca422c42b5087555e # Parent 0473eab69c6d5358b7cc5e5074b510f4927bfdec Separated out common functionality, also providing access to the clock register via the peripheral abstraction. diff -r 0473eab69c6d -r fd56764dd696 pkg/devices/lib/cpm/include/cpm-common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/cpm/include/cpm-common.h Sat Sep 16 16:53:06 2023 +0200 @@ -0,0 +1,290 @@ +/* + * Common clock functionality. + * + * Copyright (C) 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#pragma once + +#ifdef __cplusplus + +#include +#include +#include +#include + +/* Forward declaration. */ + +class Clock_base; + + + +/* Register access type. */ + +class Cpm_regs +{ + Hw::Register_block<32> _regs; + +protected: + Clock_base **_clocks; + +public: + uint32_t exclk_freq; + + explicit Cpm_regs(l4_addr_t addr, Clock_base *clocks[], + uint32_t exclk_freq); + + // Utility methods. + + uint32_t get_field(uint32_t reg, uint32_t mask, uint8_t shift); + void set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value); + + Clock_base *get_clock(int num); +}; + + + +// Register field abstraction. + +class Field +{ + uint32_t reg; + uint32_t mask; + uint8_t bit; + bool defined; + +public: + explicit Field() + : defined(false) + { + } + + explicit Field(uint32_t reg, uint32_t mask, uint32_t bit) + : reg(reg), mask(mask), bit(bit), defined(true) + { + } + + uint32_t get_field(Cpm_regs ®s); + void set_field(Cpm_regs ®s, uint32_t value); + bool is_defined() { return defined; } + + // Undefined field object. + + static Field undefined; +}; + + + +// Clock sources. + +class Mux +{ + int _num_inputs; + enum Clock_identifiers *_inputs, _input; + +public: + explicit Mux(int num_inputs, enum Clock_identifiers inputs[]) + : _num_inputs(num_inputs), _inputs(inputs) + { + } + + explicit Mux(enum Clock_identifiers input) + : _num_inputs(1), _inputs(&_input) + { + _input = input; + } + + explicit Mux() + : _num_inputs(0), _inputs(NULL) + { + } + + int get_number() { return _num_inputs; } + enum Clock_identifiers get_input(int num); +}; + +class Source +{ + Mux _inputs; + Field _source; + +public: + explicit Source(Mux inputs, Field source) + : _inputs(inputs), _source(source) + { + } + + explicit Source(Mux inputs) + : _inputs(inputs) + { + } + + explicit Source() + { + } + + int get_number() { return _inputs.get_number(); } + enum Clock_identifiers get_input(int num) { return _inputs.get_input(num); } + + // Clock source. + + uint8_t get_source(Cpm_regs ®s); + void set_source(Cpm_regs ®s, uint8_t source); + + // Clock source frequency. + + uint32_t get_frequency(Cpm_regs ®s); + + // Undefined source object. + + static Source undefined; +}; + + + +// Common clock abstraction. + +class Clock_base +{ + Source _source; + +public: + explicit Clock_base(Source source) + : _source(source) + { + } + + // Clock control. + + virtual int have_clock(Cpm_regs ®s); + virtual void start_clock(Cpm_regs ®s); + virtual void stop_clock(Cpm_regs ®s); + + // Clock divider. + + virtual uint32_t get_divider(Cpm_regs ®s); + virtual void set_divider(Cpm_regs ®s, uint32_t division); + + // Clock source. + + virtual uint8_t get_source(Cpm_regs ®s); + virtual void set_source(Cpm_regs ®s, uint8_t source); + + // Clock source frequency. + + virtual uint32_t get_source_frequency(Cpm_regs ®s); + + // Output frequency. + + virtual uint32_t get_frequency(Cpm_regs ®s); +}; + + + +// PLL descriptions. + +class Pll : public Clock_base +{ + Field _enable, _stable, _bypass; + Field _multiplier, _input_division, _output_division0, _output_division1; + +public: + explicit Pll(Source source, + Field enable, Field stable, Field bypass, + Field multiplier, Field input_division, + Field output_division0, Field output_division1) + : Clock_base(source), + _enable(enable), _stable(stable), _bypass(bypass), + _multiplier(multiplier), _input_division(input_division), + _output_division0(output_division0), _output_division1(output_division1) + { + } + + // PLL_specific control. + + int have_pll(Cpm_regs ®s); + int pll_enabled(Cpm_regs ®s); + int pll_bypassed(Cpm_regs ®s); + + // Clock control. + + int have_clock(Cpm_regs ®s); + void start_clock(Cpm_regs ®s); + void stop_clock(Cpm_regs ®s); + + // General frequency modifiers. + + uint16_t get_multiplier(Cpm_regs ®s); + void set_multiplier(Cpm_regs ®s, uint16_t multiplier); + uint8_t get_input_division(Cpm_regs ®s); + void set_input_division(Cpm_regs ®s, uint8_t divider); + uint8_t get_output_division(Cpm_regs ®s); + void set_output_division(Cpm_regs ®s, uint8_t divider); + + // PLL output frequency. + + uint32_t get_frequency(Cpm_regs ®s); + + // Other operations. + + void set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, + uint8_t in_divider, uint8_t out_divider); +}; + + + +// Clock descriptions. + +class Clock : public Clock_base +{ + Field _gate, _change_enable, _busy, _divider; + + // Clock control. + + void change_disable(Cpm_regs ®s); + void change_enable(Cpm_regs ®s); + void wait_busy(Cpm_regs ®s); + +public: + explicit Clock(Source source = Source::undefined, + Field gate = Field::undefined, + Field change_enable = Field::undefined, + Field busy = Field::undefined, + Field divider = Field::undefined) + : Clock_base(source), + _gate(gate), _change_enable(change_enable), _busy(busy), _divider(divider) + { + } + + // Clock control. + + int have_clock(Cpm_regs ®s); + void start_clock(Cpm_regs ®s); + void stop_clock(Cpm_regs ®s); + + // Clock divider. + + uint32_t get_divider(Cpm_regs ®s); + void set_divider(Cpm_regs ®s, uint32_t division); + + // Clock source. + + void set_source(Cpm_regs ®s, uint8_t source); +}; + +#endif /* __cplusplus */ diff -r 0473eab69c6d -r fd56764dd696 pkg/devices/lib/cpm/include/cpm-x1600.h --- a/pkg/devices/lib/cpm/include/cpm-x1600.h Sat Sep 16 14:28:01 2023 +0200 +++ b/pkg/devices/lib/cpm/include/cpm-x1600.h Sat Sep 16 16:53:06 2023 +0200 @@ -22,7 +22,6 @@ #pragma once #include - #include #include @@ -31,23 +30,7 @@ #ifdef __cplusplus #include - -/* Register access type. */ - -class Cpm_regs -{ - Hw::Register_block<32> _regs; - -public: - uint32_t exclk_freq; - - explicit Cpm_regs(l4_addr_t addr, uint32_t exclk_freq); - - // Utility methods. - - uint32_t get_field(uint32_t reg, uint32_t mask, uint8_t shift); - void set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value); -}; +#include /* A simple abstraction for accessing the CPM registers. * A proper device could inherit from Hw::Device and use an diff -r 0473eab69c6d -r fd56764dd696 pkg/devices/lib/cpm/src/Makefile --- a/pkg/devices/lib/cpm/src/Makefile Sat Sep 16 14:28:01 2023 +0200 +++ b/pkg/devices/lib/cpm/src/Makefile Sat Sep 16 16:53:06 2023 +0200 @@ -4,7 +4,7 @@ TARGET = libcpm.o.a libcpm.o.so PC_FILENAME := libdrivers-cpm -SRC_CC := jz4730.cc jz4740.cc jz4780.cc x1600.cc +SRC_CC := common.cc jz4730.cc jz4740.cc jz4780.cc x1600.cc PRIVATE_INCDIR += $(PKGDIR)/lib/cpm/include diff -r 0473eab69c6d -r fd56764dd696 pkg/devices/lib/cpm/src/common.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/cpm/src/common.cc Sat Sep 16 16:53:06 2023 +0200 @@ -0,0 +1,420 @@ +/* + * Common clock functionality. + * + * Copyright (C) 2023 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include + +#include "cpm-common.h" +#include + + + +// Register access. + +Cpm_regs::Cpm_regs(l4_addr_t addr, Clock_base *clocks[], + uint32_t exclk_freq) +: _clocks(clocks), exclk_freq(exclk_freq) +{ + _regs = new Hw::Mmio_register_block<32>(addr); +} + +// Utility methods. + +uint32_t +Cpm_regs::get_field(uint32_t reg, uint32_t mask, uint8_t shift) +{ + return (_regs[reg] & (mask << shift)) >> shift; +} + +void +Cpm_regs::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) +{ + _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); +} + +Clock_base * +Cpm_regs::get_clock(int num) +{ + return _clocks[num]; +} + + + +// Field methods. + +uint32_t +Field::get_field(Cpm_regs ®s) +{ + if (defined) + return regs.get_field(reg, mask, bit); + else + return 0; +} + +void +Field::set_field(Cpm_regs ®s, uint32_t value) +{ + if (defined) + regs.set_field(reg, mask, bit, value); +} + +// Undefined field. + +Field Field::undefined; + + + +// Clock sources. + +enum Clock_identifiers +Mux::get_input(int num) +{ + if (num < _num_inputs) + return _inputs[num]; + else + return Clock_undefined; +} + +// Clock sources. + +uint8_t +Source::get_source(Cpm_regs ®s) +{ + if (_source.is_defined()) + return _source.get_field(regs); + else + return 0; +} + +void +Source::set_source(Cpm_regs ®s, uint8_t source) +{ + if (!_source.is_defined()) + return; + + _source.set_field(regs, source); +} + +// Clock source frequencies. + +uint32_t +Source::get_frequency(Cpm_regs ®s) +{ + // Return the external clock frequency without any input clock. + + if (get_number() == 0) + return regs.exclk_freq; + + // Clocks with one source yield that input frequency. + + else if (get_number() == 1) + return regs.get_clock(get_input(0))->get_frequency(regs); + + // With multiple sources, obtain the selected source for the clock. + + uint8_t source = get_source(regs); + enum Clock_identifiers input = get_input(source); + + // Return the frequency of the source. + + if (input != Clock_undefined) + return regs.get_clock(input)->get_frequency(regs); + else + return 0; +} + +// Undefined source. + +Source Source::undefined; + + + +// Clock control. + +int +Clock_base::have_clock(Cpm_regs ®s) +{ + (void) regs; + return true; +} + +void +Clock_base::start_clock(Cpm_regs ®s) +{ + (void) regs; +} + +void +Clock_base::stop_clock(Cpm_regs ®s) +{ + (void) regs; +} + +// Default divider. + +uint32_t +Clock_base::get_divider(Cpm_regs ®s) +{ + (void) regs; + return 1; +} + +void +Clock_base::set_divider(Cpm_regs ®s, uint32_t division) +{ + (void) regs; + (void) division; +} + +// Clock sources. + +uint8_t +Clock_base::get_source(Cpm_regs ®s) +{ + return _source.get_source(regs); +} + +void +Clock_base::set_source(Cpm_regs ®s, uint8_t source) +{ + _source.set_source(regs, source); +} + +// Clock source frequencies. + +uint32_t +Clock_base::get_source_frequency(Cpm_regs ®s) +{ + return _source.get_frequency(regs); +} + +// Output clock frequencies. + +uint32_t +Clock_base::get_frequency(Cpm_regs ®s) +{ + return get_source_frequency(regs) / get_divider(regs); +} + + + +// PLL-specific control. + +int +Pll::have_pll(Cpm_regs ®s) +{ + return _stable.get_field(regs); +} + +int +Pll::pll_enabled(Cpm_regs ®s) +{ + return _enable.get_field(regs); +} + +int +Pll::pll_bypassed(Cpm_regs ®s) +{ + return _bypass.get_field(regs); +} + +// Clock control. + +int +Pll::have_clock(Cpm_regs ®s) +{ + return have_pll(regs) && pll_enabled(regs); +} + +void +Pll::start_clock(Cpm_regs ®s) +{ + _enable.set_field(regs, 1); + while (!have_pll(regs)); +} + +void +Pll::stop_clock(Cpm_regs ®s) +{ + _enable.set_field(regs, 0); + while (have_pll(regs)); +} + +// Feedback (13-bit) multiplier. + +uint16_t +Pll::get_multiplier(Cpm_regs ®s) +{ + return _multiplier.get_field(regs) + 1; +} + +void +Pll::set_multiplier(Cpm_regs ®s, uint16_t multiplier) +{ + _multiplier.set_field(regs, multiplier - 1); +} + +// Input (6-bit) divider. + +uint8_t +Pll::get_input_division(Cpm_regs ®s) +{ + return _input_division.get_field(regs) + 1; +} + +void +Pll::set_input_division(Cpm_regs ®s, uint8_t divider) +{ + _input_division.set_field(regs, divider - 1); +} + +// Output (dual 3-bit) dividers. + +uint8_t +Pll::get_output_division(Cpm_regs ®s) +{ + uint8_t d0 = _output_division0.get_field(regs); + uint8_t d1 = _output_division1.get_field(regs); + + return d0 * d1; +} + +void +Pll::set_output_division(Cpm_regs ®s, uint8_t divider) +{ + // Assert 1 as a minimum. + // Divider 0 must be less than or equal to divider 1. + + uint8_t d0 = (uint8_t) floor(sqrt(divider ? divider : 1)); + uint8_t d1 = divider / d0; + + _output_division0.set_field(regs, d0); + _output_division1.set_field(regs, d1); +} + +uint32_t +Pll::get_frequency(Cpm_regs ®s) +{ + // Test for PLL enable and not PLL bypass. + + if (pll_enabled(regs)) + { + if (!pll_bypassed(regs)) + return (get_source_frequency(regs) * get_multiplier(regs)) / + (get_input_division(regs) * get_output_division(regs)); + else + return get_source_frequency(regs); + } + else + return 0; +} + +void +Pll::set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) +{ + set_multiplier(regs, multiplier); + set_input_division(regs, in_divider); + set_output_division(regs, out_divider); + + if (pll_enabled(regs) && !pll_bypassed(regs)) + while (!have_pll(regs)); +} + + + +// Clock control. + +void +Clock::change_disable(Cpm_regs ®s) +{ + if (_change_enable.is_defined()) + _change_enable.set_field(regs, 0); +} + +void +Clock::change_enable(Cpm_regs ®s) +{ + if (_change_enable.is_defined()) + _change_enable.set_field(regs, 1); +} + +int +Clock::have_clock(Cpm_regs ®s) +{ + if (_gate.is_defined()) + return !_gate.get_field(regs); + else + return true; +} + +void +Clock::start_clock(Cpm_regs ®s) +{ + if (_gate.is_defined()) + _gate.set_field(regs, 0); +} + +void +Clock::stop_clock(Cpm_regs ®s) +{ + if (_gate.is_defined()) + _gate.set_field(regs, 1); +} + +void +Clock::wait_busy(Cpm_regs ®s) +{ + if (_busy.is_defined()) + while (_busy.get_field(regs)); +} + + + +// Clock dividers. + +uint32_t +Clock::get_divider(Cpm_regs ®s) +{ + if (_divider.is_defined()) + return _divider.get_field(regs) + 1; + else + return 1; +} + +void +Clock::set_divider(Cpm_regs ®s, uint32_t division) +{ + if (!_divider.is_defined()) + return; + + change_enable(regs); + _divider.set_field(regs, division - 1); + wait_busy(regs); + change_disable(regs); +} + +void +Clock::set_source(Cpm_regs ®s, uint8_t source) +{ + change_enable(regs); + Clock_base::set_source(regs, source); + wait_busy(regs); + change_disable(regs); +} diff -r 0473eab69c6d -r fd56764dd696 pkg/devices/lib/cpm/src/x1600.cc --- a/pkg/devices/lib/cpm/src/x1600.cc Sat Sep 16 14:28:01 2023 +0200 +++ b/pkg/devices/lib/cpm/src/x1600.cc Sat Sep 16 16:53:06 2023 +0200 @@ -85,236 +85,6 @@ -// Register field abstraction. - -class Field -{ - uint32_t reg; - uint32_t mask; - uint8_t bit; - bool defined; - -public: - explicit Field() - : defined(false) - { - } - - explicit Field(uint32_t reg, uint32_t mask, uint32_t bit) - : reg(reg), mask(mask), bit(bit), defined(true) - { - } - - uint32_t get_field(Cpm_regs ®s); - void set_field(Cpm_regs ®s, uint32_t value); - bool is_defined() { return defined; } -}; - -// Undefined fields. - -Field Gate_undefined, Change_enable_undefined, Busy_undefined, Divider_undefined; - - - -// Clock sources. - -class Mux -{ - int _num_inputs; - enum Clock_identifiers *_inputs, _input; - -public: - explicit Mux(int num_inputs, enum Clock_identifiers inputs[]) - : _num_inputs(num_inputs), _inputs(inputs) - { - } - - explicit Mux(enum Clock_identifiers input) - : _num_inputs(1), _inputs(&_input) - { - _input = input; - } - - explicit Mux() - : _num_inputs(0), _inputs(NULL) - { - } - - int get_number() { return _num_inputs; } - enum Clock_identifiers get_input(int num); -}; - -class Source -{ - Mux _inputs; - Field _source; - -public: - explicit Source(Mux inputs, Field source) - : _inputs(inputs), _source(source) - { - } - - explicit Source(Mux inputs) - : _inputs(inputs) - { - } - - explicit Source() - { - } - - int get_number() { return _inputs.get_number(); } - enum Clock_identifiers get_input(int num) { return _inputs.get_input(num); } - - // Clock source. - - uint8_t get_source(Cpm_regs ®s); - void set_source(Cpm_regs ®s, uint8_t source); - - // Clock source frequency. - - uint32_t get_frequency(Cpm_regs ®s); -}; - -// Undefined sources. - -Source Source_undefined; - - - -// Common clock abstraction. - -class Clock_base -{ - Source _source; - -public: - explicit Clock_base(Source source) - : _source(source) - { - } - - // Clock control. - - virtual int have_clock(Cpm_regs ®s); - virtual void start_clock(Cpm_regs ®s); - virtual void stop_clock(Cpm_regs ®s); - - // Clock divider. - - virtual uint32_t get_divider(Cpm_regs ®s); - virtual void set_divider(Cpm_regs ®s, uint32_t division); - - // Clock source. - - virtual uint8_t get_source(Cpm_regs ®s); - virtual void set_source(Cpm_regs ®s, uint8_t source); - - // Clock source frequency. - - virtual uint32_t get_source_frequency(Cpm_regs ®s); - - // Output frequency. - - virtual uint32_t get_frequency(Cpm_regs ®s); -}; - - - -// PLL descriptions. - -class Pll : public Clock_base -{ - Field _enable, _stable, _bypass; - Field _multiplier, _input_division, _output_division0, _output_division1; - -public: - explicit Pll(Source source, - Field enable, Field stable, Field bypass, - Field multiplier, Field input_division, - Field output_division0, Field output_division1) - : Clock_base(source), - _enable(enable), _stable(stable), _bypass(bypass), - _multiplier(multiplier), _input_division(input_division), - _output_division0(output_division0), _output_division1(output_division1) - { - } - - // PLL_specific control. - - int have_pll(Cpm_regs ®s); - int pll_enabled(Cpm_regs ®s); - int pll_bypassed(Cpm_regs ®s); - - // Clock control. - - int have_clock(Cpm_regs ®s); - void start_clock(Cpm_regs ®s); - void stop_clock(Cpm_regs ®s); - - // General frequency modifiers. - - uint16_t get_multiplier(Cpm_regs ®s); - void set_multiplier(Cpm_regs ®s, uint16_t multiplier); - uint8_t get_input_division(Cpm_regs ®s); - void set_input_division(Cpm_regs ®s, uint8_t divider); - uint8_t get_output_division(Cpm_regs ®s); - void set_output_division(Cpm_regs ®s, uint8_t divider); - - // PLL output frequency. - - uint32_t get_frequency(Cpm_regs ®s); - - // Other operations. - - void set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, - uint8_t in_divider, uint8_t out_divider); -}; - - - -// Clock descriptions. - -class Clock : public Clock_base -{ - Field _gate, _change_enable, _busy, _divider; - - // Clock control. - - void change_disable(Cpm_regs ®s); - void change_enable(Cpm_regs ®s); - void wait_busy(Cpm_regs ®s); - -public: - explicit Clock(Source source = Source_undefined, - Field gate = Gate_undefined, - Field change_enable = Change_enable_undefined, - Field busy = Busy_undefined, - Field divider = Divider_undefined) - : Clock_base(source), - _gate(gate), _change_enable(change_enable), _busy(busy), _divider(divider) - { - } - - // Clock control. - - int have_clock(Cpm_regs ®s); - void start_clock(Cpm_regs ®s); - void stop_clock(Cpm_regs ®s); - - // Clock divider. - - uint32_t get_divider(Cpm_regs ®s); - void set_divider(Cpm_regs ®s, uint32_t division); - - // Clock source. - - void set_source(Cpm_regs ®s, uint8_t source); -}; - - - // Register field definitions. Field Clock_source_main (Clock_control, 3, 30); // SEL_SRC (output to SCLK_A) @@ -507,7 +277,7 @@ Clock_divider_cim); Clock clock_cpu(Source(mux_core, Clock_source_cpu), - Gate_undefined, + Field::undefined, Clock_change_enable_cpu, Clock_busy_cpu, Clock_divider_cpu); @@ -527,13 +297,13 @@ Clock clock_hclock0(Source(mux_core, Clock_source_hclock0), Clock_gate_ahb0, Clock_change_enable_ahb0, - Busy_undefined, + Field::undefined, Clock_divider_hclock0); Clock clock_hclock2(Source(mux_ahb2_apb), Clock_gate_apb0, Clock_change_enable_ahb2, - Busy_undefined, + Field::undefined, Clock_divider_hclock2); Clock clock_hdmi; @@ -595,8 +365,8 @@ Clock clock_pclock(Source(mux_ahb2_apb), Clock_gate_apb0, - Change_enable_undefined, - Busy_undefined, + Field::undefined, + Field::undefined, Clock_divider_pclock); Pll clock_pll_A(Source(mux_external), @@ -730,392 +500,12 @@ -// Register access. - -Cpm_regs::Cpm_regs(l4_addr_t addr, uint32_t exclk_freq) -: exclk_freq(exclk_freq) -{ - _regs = new Hw::Mmio_register_block<32>(addr); -} - -// Utility methods. - -uint32_t -Cpm_regs::get_field(uint32_t reg, uint32_t mask, uint8_t shift) -{ - return (_regs[reg] & (mask << shift)) >> shift; -} - -void -Cpm_regs::set_field(uint32_t reg, uint32_t mask, uint8_t shift, uint32_t value) -{ - _regs[reg] = (_regs[reg] & (~(mask << shift))) | ((mask & value) << shift); -} - - - -// Field methods. - -uint32_t -Field::get_field(Cpm_regs ®s) -{ - if (defined) - return regs.get_field(reg, mask, bit); - else - return 0; -} - -void -Field::set_field(Cpm_regs ®s, uint32_t value) -{ - if (defined) - regs.set_field(reg, mask, bit, value); -} - - - -// Clock sources. - -enum Clock_identifiers -Mux::get_input(int num) -{ - if (num < _num_inputs) - return _inputs[num]; - else - return Clock_undefined; -} - -// Clock sources. - -uint8_t -Source::get_source(Cpm_regs ®s) -{ - if (_source.is_defined()) - return _source.get_field(regs); - else - return 0; -} - -void -Source::set_source(Cpm_regs ®s, uint8_t source) -{ - if (!_source.is_defined()) - return; - - _source.set_field(regs, source); -} - -// Clock source frequencies. - -uint32_t -Source::get_frequency(Cpm_regs ®s) -{ - // Return the external clock frequency without any input clock. - - if (get_number() == 0) - return regs.exclk_freq; - - // Clocks with one source yield that input frequency. - - else if (get_number() == 1) - return clocks[get_input(0)]->get_frequency(regs); - - // With multiple sources, obtain the selected source for the clock. - - uint8_t source = get_source(regs); - enum Clock_identifiers input = get_input(source); - - // Return the frequency of the source. - - if (input != Clock_undefined) - return clocks[input]->get_frequency(regs); - else - return 0; -} - - - -// Clock control. - -int -Clock_base::have_clock(Cpm_regs ®s) -{ - (void) regs; - return true; -} - -void -Clock_base::start_clock(Cpm_regs ®s) -{ - (void) regs; -} - -void -Clock_base::stop_clock(Cpm_regs ®s) -{ - (void) regs; -} - -// Default divider. - -uint32_t -Clock_base::get_divider(Cpm_regs ®s) -{ - (void) regs; - return 1; -} - -void -Clock_base::set_divider(Cpm_regs ®s, uint32_t division) -{ - (void) regs; - (void) division; -} - -// Clock sources. - -uint8_t -Clock_base::get_source(Cpm_regs ®s) -{ - return _source.get_source(regs); -} - -void -Clock_base::set_source(Cpm_regs ®s, uint8_t source) -{ - _source.set_source(regs, source); -} - -// Clock source frequencies. - -uint32_t -Clock_base::get_source_frequency(Cpm_regs ®s) -{ - return _source.get_frequency(regs); -} - -// Output clock frequencies. - -uint32_t -Clock_base::get_frequency(Cpm_regs ®s) -{ - return get_source_frequency(regs) / get_divider(regs); -} - - - -// PLL-specific control. - -int -Pll::have_pll(Cpm_regs ®s) -{ - return _stable.get_field(regs); -} - -int -Pll::pll_enabled(Cpm_regs ®s) -{ - return _enable.get_field(regs); -} - -int -Pll::pll_bypassed(Cpm_regs ®s) -{ - return _bypass.get_field(regs); -} - -// Clock control. - -int -Pll::have_clock(Cpm_regs ®s) -{ - return have_pll(regs) && pll_enabled(regs); -} - -void -Pll::start_clock(Cpm_regs ®s) -{ - _enable.set_field(regs, 1); - while (!have_pll(regs)); -} - -void -Pll::stop_clock(Cpm_regs ®s) -{ - _enable.set_field(regs, 0); - while (have_pll(regs)); -} - -// Feedback (13-bit) multiplier. - -uint16_t -Pll::get_multiplier(Cpm_regs ®s) -{ - return _multiplier.get_field(regs) + 1; -} - -void -Pll::set_multiplier(Cpm_regs ®s, uint16_t multiplier) -{ - _multiplier.set_field(regs, multiplier - 1); -} - -// Input (6-bit) divider. - -uint8_t -Pll::get_input_division(Cpm_regs ®s) -{ - return _input_division.get_field(regs) + 1; -} - -void -Pll::set_input_division(Cpm_regs ®s, uint8_t divider) -{ - _input_division.set_field(regs, divider - 1); -} - -// Output (dual 3-bit) dividers. - -uint8_t -Pll::get_output_division(Cpm_regs ®s) -{ - uint8_t d0 = _output_division0.get_field(regs); - uint8_t d1 = _output_division1.get_field(regs); - - return d0 * d1; -} - -void -Pll::set_output_division(Cpm_regs ®s, uint8_t divider) -{ - // Assert 1 as a minimum. - // Divider 0 must be less than or equal to divider 1. - - uint8_t d0 = (uint8_t) floor(sqrt(divider ? divider : 1)); - uint8_t d1 = divider / d0; - - _output_division0.set_field(regs, d0); - _output_division1.set_field(regs, d1); -} - -uint32_t -Pll::get_frequency(Cpm_regs ®s) -{ - // Test for PLL enable and not PLL bypass. - - if (pll_enabled(regs)) - { - if (!pll_bypassed(regs)) - return (get_source_frequency(regs) * get_multiplier(regs)) / - (get_input_division(regs) * get_output_division(regs)); - else - return get_source_frequency(regs); - } - else - return 0; -} - -void -Pll::set_pll_parameters(Cpm_regs ®s, uint16_t multiplier, uint8_t in_divider, uint8_t out_divider) -{ - set_multiplier(regs, multiplier); - set_input_division(regs, in_divider); - set_output_division(regs, out_divider); - - if (pll_enabled(regs) && !pll_bypassed(regs)) - while (!have_pll(regs)); -} - - - -// Clock control. - -void -Clock::change_disable(Cpm_regs ®s) -{ - if (_change_enable.is_defined()) - _change_enable.set_field(regs, 0); -} - -void -Clock::change_enable(Cpm_regs ®s) -{ - if (_change_enable.is_defined()) - _change_enable.set_field(regs, 1); -} - -int -Clock::have_clock(Cpm_regs ®s) -{ - if (_gate.is_defined()) - return !_gate.get_field(regs); - else - return true; -} - -void -Clock::start_clock(Cpm_regs ®s) -{ - if (_gate.is_defined()) - _gate.set_field(regs, 0); -} - -void -Clock::stop_clock(Cpm_regs ®s) -{ - if (_gate.is_defined()) - _gate.set_field(regs, 1); -} - -void -Clock::wait_busy(Cpm_regs ®s) -{ - if (_busy.is_defined()) - while (_busy.get_field(regs)); -} - - - -// Clock dividers. - -uint32_t -Clock::get_divider(Cpm_regs ®s) -{ - if (_divider.is_defined()) - return _divider.get_field(regs) + 1; - else - return 1; -} - -void -Clock::set_divider(Cpm_regs ®s, uint32_t division) -{ - if (!_divider.is_defined()) - return; - - change_enable(regs); - _divider.set_field(regs, division - 1); - wait_busy(regs); - change_disable(regs); -} - -void -Clock::set_source(Cpm_regs ®s, uint8_t source) -{ - change_enable(regs); - Clock_base::set_source(regs, source); - wait_busy(regs); - change_disable(regs); -} - - - // If implemented as a Hw::Device, various properties would be // initialised in the constructor and obtained from the device tree // definitions. Cpm_x1600_chip::Cpm_x1600_chip(l4_addr_t addr, uint32_t exclk_freq) -: _cpm_regs(addr, exclk_freq) +: _cpm_regs(addr, clocks, exclk_freq) { // add_cid("cpm"); // add_cid("cpm-x1600");