C/C++ API Reference
Loading...
Searching...
No Matches
sync_selector.h
1// Copyright 2026 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15#pragma once
16
17#include <mutex>
18
19#include "fsl_clock.h"
20#include "pw_assert/assert.h"
21#include "pw_clock_tree/clock_tree.h"
22#include "pw_status/status.h"
23#include "pw_status/try.h"
24
25namespace pw::clock_tree {
26
32template <typename ElementType>
33class ClockMcuxpressoSyncSelector : public DependentElement<ElementType> {
34 public:
35 static constexpr clock_attach_id_t kNoSelector =
36 static_cast<clock_attach_id_t>(0);
37
48 template <typename SourceType>
50 SourceType& initial_source,
51 clock_attach_id_t initial_selector,
52 clock_attach_id_t disable_selector = kNoSelector)
53 : DependentElement<ElementType>(initial_source),
54 current_selector_(initial_selector),
55 disable_selector_(disable_selector) {
56 PW_ASSERT(initial_selector != kNoSelector);
57 }
58
59 template <typename SourceType>
60 pw::Status ChangeSource(SourceType& new_source,
61 clock_attach_id_t new_selector) {
62 PW_ASSERT(new_selector != kNoSelector);
63
64 std::lock_guard lock(this->lock());
65
66 if (this->current_selector_ == new_selector) {
67 return pw::OkStatus();
68 }
69 Element& current_source = this->source();
70
71 // This clock element might be in an enabled or disabled state!
72 //
73 // We only switch the selector if:
74 // 1. This element is logically enabled (ref_count() > 0).
75 // We must switch the running mux to the new source.
76 // OR
77 // 2. This element is logically disabled, but the mux does not support a
78 // disable selector (the mux is physically always-on.) We must perform
79 // the switch (and the synchronized mux dance) to ensure the mux points
80 // to the new source, even though this element is logically off.
81 // Note that in this case, the clock will run briefly!
82 const bool is_enabled = (this->ref_count() > 0);
83 const bool always_on_mux = (disable_selector_ == kNoSelector);
84
85 if (is_enabled || always_on_mux) {
86 // Hardware switch required.
87
88 // Ensure the old source is running (sync clock mux requirement).
89 // If this element is enabled, we already hold a ref to current_source.
90 // If this element is disabled (but always_on_mux), we need a temporary
91 // ref.
92 bool acquired_temp_current_ref = false;
93 if (!is_enabled) {
94 PW_TRY(current_source.Acquire());
95 acquired_temp_current_ref = true;
96 }
97
98 // Ensure the new source is running (sync clock mux requirement).
99 // We acquire it now. If we are enabled, this becomes our permanent ref.
100 // If we are disabled, this is a temporary ref for the switch.
101 if constexpr (SourceType::kMayFail) {
102 if (pw::Status status = new_source.Acquire(); !status.ok()) {
103 if (acquired_temp_current_ref) {
104 current_source.Release().IgnoreError();
105 }
106 return status;
107 }
108 } else {
109 new_source.Acquire();
110 }
111
112 // Perform the hardware switch.
113 CLOCK_AttachClk(new_selector);
114
115 // Cleanup the old source.
116 // We always release the old source.
117 // If is_enabled: We are dropping our permanent ref.
118 // If !is_enabled: We are dropping the temporary ref we took above.
119 current_source.Release().IgnoreError();
120
121 // Cleanup the new source (if necessary).
122 if (!is_enabled) {
123 // This element is disabled, so we cannot hold on to the reference
124 // taken above.
125 if constexpr (SourceType::kMayFail) {
126 new_source.Release().IgnoreError();
127 } else {
128 new_source.Release();
129 }
130 }
131 }
132
133 // Update internal state regardless of whether we touched HW.
134 // If we didn't touch HW, DoAcquireLocked() / DoEnable() will use these new
135 // values later.
136 this->SetSource(new_source);
137 current_selector_ = new_selector;
138
139 return pw::OkStatus();
140 }
141
142 private:
143 clock_attach_id_t current_selector_;
144 const clock_attach_id_t disable_selector_;
145
146 // pw::clock_tree::Element impl.
147
148 // NOTE: DoEnable() and DoDisable() are marked 'final' and cannot fail.
149 // This allows internal callers to skip error checking.
150 pw::Status DoEnable() final {
151 PW_ASSERT(current_selector_ != kNoSelector);
152 CLOCK_AttachClk(current_selector_);
153 return pw::OkStatus();
154 }
155
156 pw::Status DoDisable() final {
157 if (disable_selector_ != kNoSelector) {
158 CLOCK_AttachClk(disable_selector_);
159 }
160 return pw::OkStatus();
161 }
162};
163
167
172
177
178} // namespace pw::clock_tree
Definition: status.h:120
constexpr bool ok() const
Definition: status.h:346
Definition: sync_selector.h:33
constexpr ClockMcuxpressoSyncSelector(SourceType &initial_source, clock_attach_id_t initial_selector, clock_attach_id_t disable_selector=kNoSelector)
Definition: sync_selector.h:49
Definition: clock_tree.h:338
void SetSource(SourceType &source)
Definition: clock_tree.h:405
#define PW_TRY(expr)
Returns early if expr is a non-OK Status or Result.
Definition: try.h:27
constexpr Status OkStatus()
Definition: status.h:450
Clock tree management library.
Definition: clock_tree.h:30