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