xref: /aosp_15_r20/external/pigweed/pw_spi_mcuxpresso/public/pw_spi_mcuxpresso/responder.h (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 #pragma once
15 
16 #include <atomic>
17 #include <cstdint>
18 
19 #include "fsl_spi.h"
20 #include "fsl_spi_dma.h"
21 #include "lib/stdcompat/utility.h"
22 #include "pw_assert/check.h"
23 #include "pw_dma_mcuxpresso/dma.h"
24 #include "pw_spi/initiator.h"  // Reuse config defs
25 #include "pw_spi/responder.h"
26 #include "pw_status/status.h"
27 
28 namespace pw::spi {
29 
30 enum class FifoErrorCheck {
31   kNone,     // Don't check for FIFO error.
32   kLogOnly,  // Only log on FIFO error.
33   kError,    // Log and return DATA_LOSS.
34 };
35 
36 class McuxpressoResponder : public Responder {
37  public:
38   struct Config {
39     ClockPolarity polarity;
40     ClockPhase phase;
41     BitsPerWord bits_per_word;
42     BitOrder bit_order;
43     uint32_t base_address;  // Flexcomm peripheral base address.
44     // True if the driver should handle Chip Select (CS) assertion and
45     // deassertion. When set, transfers will complete on CS deassertion.
46     bool handle_cs = true;
47 
48     // If enabled, the FIFO status registers are checked for error
49     // (underflow/overflow) upon transfer completion, returning DATA_LOSS if
50     // detected.
51     //
52     // NOTE: A false positive could be triggered if this is enabled and the
53     // initiator clocks more bytes than the transfer is set up to send+receive.
54     FifoErrorCheck check_fifo_error = FifoErrorCheck::kNone;
55   };
56 
McuxpressoResponder(const Config config,dma::McuxpressoDmaChannel & tx_dma,dma::McuxpressoDmaChannel & rx_dma)57   McuxpressoResponder(const Config config,
58                       dma::McuxpressoDmaChannel& tx_dma,
59                       dma::McuxpressoDmaChannel& rx_dma)
60       : config_(config),
61         base_(reinterpret_cast<SPI_Type*>(config.base_address)),
62         tx_dma_(tx_dma),
63         rx_dma_(rx_dma) {}
64 
~McuxpressoResponder()65   ~McuxpressoResponder() { PW_CRASH("Destruction not supported"); }
66 
67   McuxpressoResponder(const McuxpressoResponder&) = delete;
68   McuxpressoResponder& operator=(const McuxpressoResponder&) = delete;
69   McuxpressoResponder(const McuxpressoResponder&&) = delete;
70   McuxpressoResponder& operator=(const McuxpressoResponder&&) = delete;
71 
72   Status Initialize();
73 
74  private:
75   // pw::spi::Responder impl.
DoSetCompletionHandler(Function<void (ByteSpan,Status)> callback)76   void DoSetCompletionHandler(
77       Function<void(ByteSpan, Status)> callback) override {
78     completion_callback_ = std::move(callback);
79   };
80   Status DoWriteReadAsync(ConstByteSpan tx_data, ByteSpan rx_data) override;
81   void DoCancel() override;
82 
83   static void SdkCallback(SPI_Type* base,
84                           spi_dma_handle_t* handle,
85                           status_t sdk_status,
86                           void* userData);
87   void DmaComplete(status_t sdk_status);
88 
89   static void FlexcommSpiIrqHandler(void* base, void* handle);
90   void CsAsserted();
91   void CsDeasserted();
92   Status WaitForQuiescenceAfterCsDeassertion();
93 
94   void TransferComplete(Status status, size_t bytes_transferred);
95 
96   const Config config_;
97   SPI_Type* base_;
98   spi_dma_handle_t handle_;
99   dma::McuxpressoDmaChannel& tx_dma_;
100   dma::McuxpressoDmaChannel& rx_dma_;
101   Function<void(ByteSpan, Status)> completion_callback_;
102 
103   // Current WriteReadAsync
104   struct Transaction {
105     ByteSpan rx_data;
106 
107     explicit operator bool() const noexcept { return !rx_data.empty(); }
108   } current_transaction_;
109 
110   enum class State : uint32_t {
111     // No transaction in progress.
112     //
113     // DoWriteReadAsync: Move to kBusy
114     // SDK callback: Nothing (erroneous?)
115     // Cancel: Nothing
116     kIdle = 0,
117 
118     // Transaction started, waiting for SDK callback or cancellation.
119     //
120     // DoWriteReadAsync: return error
121     // SDK callback: Complete, call callback and move to kIdle
122     // Cancel: Cancel, call callback and move to kIdle
123     kBusy = 1,
124   };
125   std::atomic<State> state_ = State::kIdle;
126 
127   bool TryChangeState(State expected, State desired, State* old = nullptr) {
128     if (state_.compare_exchange_strong(expected, desired)) {
129       return true;
130     }
131     if (old) {
132       *old = expected;
133     }
134     return false;
135   }
136 };
137 
138 }  // namespace pw::spi
139