• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 class McuxpressoResponder : public Responder {
31  public:
32   struct Config {
33     ClockPolarity polarity;
34     ClockPhase phase;
35     BitsPerWord bits_per_word;
36     BitOrder bit_order;
37     uint32_t base_address;  // Flexcomm peripheral base address.
38     // True if the driver should handle Chip Select (CS) assertion and
39     // deassertion. When set, transfers will complete on CS deassertion.
40     bool handle_cs;
41   };
42 
McuxpressoResponder(const Config config,dma::McuxpressoDmaChannel & tx_dma,dma::McuxpressoDmaChannel & rx_dma)43   McuxpressoResponder(const Config config,
44                       dma::McuxpressoDmaChannel& tx_dma,
45                       dma::McuxpressoDmaChannel& rx_dma)
46       : config_(config),
47         base_(reinterpret_cast<SPI_Type*>(config.base_address)),
48         tx_dma_(tx_dma),
49         rx_dma_(rx_dma) {}
50 
~McuxpressoResponder()51   ~McuxpressoResponder() { PW_CRASH("Destruction not supported"); }
52 
53   McuxpressoResponder(const McuxpressoResponder&) = delete;
54   McuxpressoResponder& operator=(const McuxpressoResponder&) = delete;
55   McuxpressoResponder(const McuxpressoResponder&&) = delete;
56   McuxpressoResponder& operator=(const McuxpressoResponder&&) = delete;
57 
58   Status Initialize();
59 
60  private:
61   // pw::spi::Responder impl.
DoSetCompletionHandler(Function<void (ByteSpan,Status)> callback)62   void DoSetCompletionHandler(
63       Function<void(ByteSpan, Status)> callback) override {
64     completion_callback_ = std::move(callback);
65   };
66   Status DoWriteReadAsync(ConstByteSpan tx_data, ByteSpan rx_data) override;
67   void DoCancel() override;
68 
69   static void SdkCallback(SPI_Type* base,
70                           spi_dma_handle_t* handle,
71                           status_t sdk_status,
72                           void* userData);
73   void DmaComplete(status_t sdk_status);
74 
75   static void FlexcommSpiIrqHandler(void* base, void* handle);
76   void CsAsserted();
77   void CsDeasserted();
78   Status WaitForQuiescenceAfterCsDeassertion();
79 
80   void TransferComplete(Status status, size_t bytes_transferred);
81 
82   const Config config_;
83   SPI_Type* base_;
84   spi_dma_handle_t handle_;
85   dma::McuxpressoDmaChannel& tx_dma_;
86   dma::McuxpressoDmaChannel& rx_dma_;
87   Function<void(ByteSpan, Status)> completion_callback_;
88 
89   // Current WriteReadAsync
90   struct Transaction {
91     ByteSpan rx_data;
92 
93     explicit operator bool() const noexcept { return !rx_data.empty(); }
94   } current_transaction_;
95 
96   enum class State : uint32_t {
97     // No transaction in progress.
98     //
99     // DoWriteReadAsync: Move to kBusy
100     // SDK callback: Nothing (erroneous?)
101     // Cancel: Nothing
102     kIdle = 0,
103 
104     // Transaction started, waiting for SDK callback or cancellation.
105     //
106     // DoWriteReadAsync: return error
107     // SDK callback: Complete, call callback and move to kIdle
108     // Cancel: Cancel, call callback and move to kIdle
109     kBusy = 1,
110   };
111   std::atomic<State> state_ = State::kIdle;
112 
113   bool TryChangeState(State expected, State desired, State* old = nullptr) {
114     if (state_.compare_exchange_strong(expected, desired)) {
115       return true;
116     }
117     if (old) {
118       *old = expected;
119     }
120     return false;
121   }
122 };
123 
124 }  // namespace pw::spi
125