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