xref: /aosp_15_r20/external/pigweed/pw_clock_tree/examples.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 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 #include "pw_assert/check.h"
16 #include "pw_clock_tree/clock_tree.h"
17 #include "pw_unit_test/framework.h"
18 
19 namespace examples {
20 
EnableClock()21 static pw::Status EnableClock() {
22   // Action to enable clock
23   return pw::OkStatus();
24 }
25 
DisableClock()26 static pw::Status DisableClock() {
27   // Action to disable clock
28   return pw::OkStatus();
29 }
30 
EnableClockDivider(uint32_t,uint32_t)31 static pw::Status EnableClockDivider(uint32_t, uint32_t) {
32   // Action to enable clock divider
33   return pw::OkStatus();
34 }
35 
SetSelector(uint32_t)36 static pw::Status SetSelector(uint32_t) {
37   // Action to set selector
38   return pw::OkStatus();
39 }
40 
41 // DOCSTAG: [pw_clock_tree-examples-ClockSourceExampleDef]
42 /// Class template implementing a clock source.
43 ///
44 /// Template argument `ElementType` can be of class `ElementBlocking`,
45 /// `ElementNonBlockingCannotFail` or
46 /// `ElementNonBlockingMightFail.`
47 template <typename ElementType>
48 class ClockSourceExample : public pw::clock_tree::ClockSource<ElementType> {
49  private:
DoEnable()50   pw::Status DoEnable() final { return EnableClock(); }
51 
DoDisable()52   pw::Status DoDisable() final { return DisableClock(); }
53 };
54 using ClockSourceExampleNonBlocking =
55     ClockSourceExample<pw::clock_tree::ElementNonBlockingCannotFail>;
56 // DOCSTAG: [pw_clock_tree-examples-ClockSourceExampleDef]
57 
58 // DOCSTAG: [pw_clock_tree-examples-ClockDividerExampleDef]
59 /// Class template implementing a clock divider.
60 ///
61 /// Template argument `ElementType` can be of class `ElementBlocking`,
62 /// `ElementNonBlockingCannotFail` or
63 /// `ElementNonBlockingMightFail.`
64 template <typename ElementType>
65 class ClockDividerExample
66     : public pw::clock_tree::ClockDividerElement<ElementType> {
67  public:
ClockDividerExample(ElementType & source,uint32_t divider_name,uint32_t divider)68   constexpr ClockDividerExample(ElementType& source,
69                                 uint32_t divider_name,
70                                 uint32_t divider)
71       : pw::clock_tree::ClockDividerElement<ElementType>(source, divider),
72         divider_name_(divider_name) {}
73 
74  private:
DoEnable()75   pw::Status DoEnable() final {
76     return EnableClockDivider(divider_name_, this->divider());
77   }
78 
79   uint32_t divider_name_;
80 };
81 using ClockDividerExampleNonBlocking =
82     ClockDividerExample<pw::clock_tree::ElementNonBlockingCannotFail>;
83 // DOCSTAG: [pw_clock_tree-examples-ClockDividerExampleDef]
84 
85 // DOCSTAG: [pw_clock_tree-examples-ClockSelectorExampleDef]
86 /// Class template implementing a clock selector.
87 ///
88 /// Template argument `ElementType` can be of class `ElementBlocking`,
89 /// `ElementNonBlockingCannotFail` or
90 /// `ElementNonBlockingMightFail.`
91 template <typename ElementType>
92 class ClockSelectorExample
93     : public pw::clock_tree::DependentElement<ElementType> {
94  public:
ClockSelectorExample(ElementType & source,uint32_t selector,uint32_t selector_enable,uint32_t selector_disable)95   constexpr ClockSelectorExample(ElementType& source,
96                                  uint32_t selector,
97                                  uint32_t selector_enable,
98                                  uint32_t selector_disable)
99       : pw::clock_tree::DependentElement<ElementType>(source),
100         selector_(selector),
101         selector_enable_(selector_enable),
102         selector_disable_(selector_disable) {}
103 
SetSource(ElementType & new_source,uint32_t new_selector_enable)104   pw::Status SetSource(ElementType& new_source, uint32_t new_selector_enable) {
105     // Store a copy of the current `selector_enable_` variable in case
106     // that the update fails, since we need to update `selector_enable_`
107     // to its new value, since `UpdateSource` might call the `DoEnable`
108     // member function.
109     uint32_t old_selector_enable = selector_enable_;
110     selector_enable_ = new_selector_enable;
111     const bool kPermitChangeIfInUse = true;
112     pw::Status status = this->UpdateSource(new_source, kPermitChangeIfInUse);
113     if (!status.ok()) {
114       // Restore the old selector value.
115       selector_enable_ = old_selector_enable;
116     }
117 
118     return status;
119   }
120 
121  private:
DoEnable()122   pw::Status DoEnable() final { return SetSelector(selector_enable_); }
DoDisable()123   pw::Status DoDisable() final { return SetSelector(selector_disable_); }
124 
125   uint32_t selector_;
126   uint32_t selector_enable_;
127   uint32_t selector_disable_;
128   template <typename U>
129   friend class ClockTreeSetSource;
130 };
131 using ClockSelectorExampleNonBlocking =
132     ClockSelectorExample<pw::clock_tree::ElementNonBlockingCannotFail>;
133 // DOCSTAG: [pw_clock_tree-examples-ClockSelectorExampleDef]
134 
135 // DOCSTAG: [pw_clock_tree-examples-ClockTreeSetSourcesExampleDef]
136 /// Class implementing a clock tree that also supports the `UpdateSource`
137 /// method of the `ClockSelectorExample` class template.
138 class ClockTreeSetSourceExample : public pw::clock_tree::ClockTree {
139  public:
140   /// SetSource could be implemented for the other clock tree element classes
141   /// as well.
SetSource(ClockSelectorExampleNonBlocking & element,pw::clock_tree::ElementNonBlockingCannotFail & new_source,uint32_t selector_enable)142   void SetSource(ClockSelectorExampleNonBlocking& element,
143                  pw::clock_tree::ElementNonBlockingCannotFail& new_source,
144                  uint32_t selector_enable) {
145     std::lock_guard lock(interrupt_spin_lock_);
146     element.SetSource(new_source, selector_enable).IgnoreError();
147   }
148 };
149 // DOCSTAG: [pw_clock_tree-examples-ClockTreeSetSourcesExampleDef]
150 
TEST(ClockTree,ClockTreeElementExample)151 TEST(ClockTree, ClockTreeElementExample) {
152   // DOCSTAG: [pw_clock_tree-examples-ClockTreeDec]
153   // Create the clock tree
154   ClockTreeSetSourceExample clock_tree;
155   // DOCSTAG: [pw_clock_tree-examples-ClockTreeDec]
156 
157   // DOCSTAG: [pw_clock_tree-examples-ClockTreeElementsDec]
158   // Define the clock tree
159   ClockSourceExampleNonBlocking clock_a;
160   ClockSourceExampleNonBlocking clock_b;
161 
162   const uint32_t kSelectorId = 7;
163   const uint32_t kSelectorEnable1 = 2;
164   const uint32_t kSelectorEnable2 = 4;
165   const uint32_t kSelectorDisable = 7;
166   // clock_selector_c depends on clock_a.
167   ClockSelectorExampleNonBlocking clock_selector_c(
168       clock_a, kSelectorId, kSelectorEnable1, kSelectorDisable);
169 
170   const uint32_t kDividerId = 12;
171   const uint32_t kDividerValue1 = 42;
172   // clock_divider_d depends on clock_b.
173   ClockDividerExampleNonBlocking clock_divider_d(
174       clock_b, kDividerId, kDividerValue1);
175   // DOCSTAG: [pw_clock_tree-examples-ClockTreeElementsDec]
176 
177   // DOCSTAG: [pw_clock_tree-examples-AcquireClockSelectorC]
178   // Acquire a reference to clock_selector_c, which will enable clock_selector_c
179   // and its dependent clock_a.
180   clock_tree.Acquire(clock_selector_c);
181   // DOCSTAG: [pw_clock_tree-examples-AcquireClockSelectorC]
182 
183   // DOCSTAG: [pw_clock_tree-examples-ChangeClockSelectorCDependentSource]
184   // Change clock_selector_c to depend on clock_divider_d.
185   // This enables clock_b and clock_divider_d, and disables clock_a.
186   clock_tree.SetSource(clock_selector_c, clock_divider_d, kSelectorEnable2);
187   // DOCSTAG: [pw_clock_tree-examples-ChangeClockSelectorCDependentSource]
188 
189   // DOCSTAG: [pw_clock_tree-examples-SetClockDividerDValue]
190   // Change the divider value for clock_divider_d.
191   const uint32_t kDividerValue2 = 21;
192   clock_tree.SetDividerValue(clock_divider_d, kDividerValue2);
193   // DOCSTAG: [pw_clock_tree-examples-SetClockDividerDValue]
194 
195   // DOCSTAG: [pw_clock_tree-examples-ReleaseClockSelectorC]
196   // Release reference to clock_selector_c, which will disable clock_selector_c,
197   // clock_divider_d, and clock_b.
198   clock_tree.Release(clock_selector_c);
199   // All clock tree elements are disabled now.
200   // DOCSTAG: [pw_clock_tree-examples-ReleaseClockSelectorC]
201 }
202 
USART_RTOS_Init()203 static pw::Status USART_RTOS_Init() { return pw::OkStatus(); }
USART_RTOS_Deinit()204 static void USART_RTOS_Deinit() {}
205 
206 // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversClassDef]
207 #include "pw_clock_tree/clock_tree.h"
208 
209 class UartStreamMcuxpresso {
210  public:
Init()211   pw::Status Init() {
212     // Acquire reference to clock before initializing device.
213     PW_TRY(clock_tree_element_controller_.Acquire());
214     pw::Status status = USART_RTOS_Init();
215     if (!status.ok()) {
216       // Failed to initialize device, release the acquired clock.
217       clock_tree_element_controller_.Release().IgnoreError();
218     }
219     return status;
220   }
221 
Deinit()222   void Deinit() {
223     // Deinitialize the device before we can release the reference
224     // to the clock.
225     USART_RTOS_Deinit();
226     clock_tree_element_controller_.Release().IgnoreError();
227   }
228 
229   // Device constructor that optionally accepts `clock_tree` and
230   // `clock_tree_element` to manage clock lifecycle.
UartStreamMcuxpresso(pw::clock_tree::ClockTree * clock_tree=nullptr,pw::clock_tree::ElementNonBlockingCannotFail * clock_tree_element=nullptr)231   constexpr UartStreamMcuxpresso(
232       pw::clock_tree::ClockTree* clock_tree = nullptr,
233       pw::clock_tree::ElementNonBlockingCannotFail* clock_tree_element =
234           nullptr)
235       : clock_tree_element_controller_(clock_tree, clock_tree_element) {}
236 
237  private:
238   pw::clock_tree::ElementController clock_tree_element_controller_;
239 };
240 // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversClassDef]
241 
242 using ClockSourceUart =
243     ClockSourceExample<pw::clock_tree::ElementNonBlockingCannotFail>;
244 
ClockTreeExample()245 pw::Status ClockTreeExample() {
246   // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversUsage]
247 
248   // Declare the clock tree
249   pw::clock_tree::ClockTree clock_tree;
250   // Declare the uart clock source
251   ClockSourceUart uart_clock_source;
252   UartStreamMcuxpresso uart(&clock_tree, &uart_clock_source);
253 
254   // Initialize the uart which enables the uart clock source.
255   PW_TRY(uart.Init());
256   PW_CHECK(uart_clock_source.ref_count() > 0);
257 
258   // Do something with uart
259 
260   // Deinitialize the uart which disable the uart clock source.
261   uart.Deinit();
262 
263   // DOCSTAG: [pw_clock_tree-examples-IntegrationIntoDeviceDriversUsage]
264 
265   return pw::OkStatus();
266 }
267 
TEST(ClockTree,ClockTreeExample)268 TEST(ClockTree, ClockTreeExample) {
269   pw::Status status = ClockTreeExample();
270   EXPECT_EQ(status.code(), PW_STATUS_OK);
271 }
272 }  // namespace examples
273