xref: /aosp_15_r20/external/pigweed/pw_spi_mcuxpresso/flexio_spi.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 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_spi_mcuxpresso/flexio_spi.h"
16 
17 #include <cinttypes>
18 #include <mutex>
19 
20 #include "fsl_flexio_spi.h"
21 #include "fsl_gpio.h"
22 #include "pw_chrono/system_clock.h"
23 #include "pw_log/log.h"
24 #include "pw_spi/initiator.h"
25 #include "pw_status/status.h"
26 #include "pw_status/try.h"
27 
28 namespace pw::spi {
29 namespace {
30 
31 using namespace ::std::literals::chrono_literals;
32 constexpr auto kMaxWait = chrono::SystemClock::for_at_least(1000ms);
33 
ToPwStatus(int32_t status)34 Status ToPwStatus(int32_t status) {
35   switch (status) {
36     // Intentional fall-through
37     case kStatus_Success:
38     case kStatus_FLEXIO_SPI_Idle:
39       return OkStatus();
40     case kStatus_ReadOnly:
41       return Status::PermissionDenied();
42     case kStatus_OutOfRange:
43       return Status::OutOfRange();
44     case kStatus_InvalidArgument:
45       return Status::InvalidArgument();
46     case kStatus_Timeout:
47       return Status::DeadlineExceeded();
48     case kStatus_NoTransferInProgress:
49       return Status::FailedPrecondition();
50     // Intentional fall-through
51     case kStatus_Fail:
52     default:
53       PW_LOG_ERROR("Mcuxpresso FlexIO_SPI unknown error code: %" PRId32,
54                    status);
55       return Status::Unknown();
56   }
57 }
58 
59 }  // namespace
60 
61 // inclusive-language: disable
62 
~McuxpressoFlexIoInitiator()63 McuxpressoFlexIoInitiator::~McuxpressoFlexIoInitiator() {
64   if (is_initialized()) {
65     FLEXIO_SPI_MasterDeinit(&flexio_spi_config_);
66   }
67 }
68 
ConfigureClock(flexio_spi_master_config_t * masterConfig,ClockPolarity clockPolarity)69 void McuxpressoFlexIoInitiator::ConfigureClock(
70     flexio_spi_master_config_t* masterConfig, ClockPolarity clockPolarity) {
71   flexio_timer_config_t timerConfig = {};
72   uint16_t timerDiv = 0;
73   uint16_t timerCmp = 0;
74 
75   // Rather than modify the flexio_spi driver code to support negative clock
76   // polarity, we duplicate the clock setup here to add support for inverting
77   // the output for SPI mode CPOL=1.
78   timerConfig.triggerSelect =
79       FLEXIO_TIMER_TRIGGER_SEL_SHIFTnSTAT(flexio_spi_config_.shifterIndex[0]);
80   timerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityActiveLow;
81   timerConfig.triggerSource = kFLEXIO_TimerTriggerSourceInternal;
82   timerConfig.pinConfig = kFLEXIO_PinConfigOutput;
83   timerConfig.pinSelect = flexio_spi_config_.SCKPinIndex;
84   if (clockPolarity == ClockPolarity::kActiveLow) {
85     timerConfig.pinPolarity = kFLEXIO_PinActiveLow;
86   } else {
87     timerConfig.pinPolarity = kFLEXIO_PinActiveHigh;
88   }
89 
90   timerConfig.timerMode = kFLEXIO_TimerModeDual8BitBaudBit;
91   timerConfig.timerOutput = kFLEXIO_TimerOutputZeroNotAffectedByReset;
92   timerConfig.timerDecrement = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;
93   timerConfig.timerReset = kFLEXIO_TimerResetNever;
94   timerConfig.timerDisable = kFLEXIO_TimerDisableOnTimerCompare;
95   timerConfig.timerEnable = kFLEXIO_TimerEnableOnTriggerHigh;
96   timerConfig.timerStop = kFLEXIO_TimerStopBitEnableOnTimerDisable;
97   timerConfig.timerStart = kFLEXIO_TimerStartBitEnabled;
98 
99   timerDiv = static_cast<uint16_t>(src_clock_hz_ / masterConfig->baudRate_Bps);
100   timerDiv = timerDiv / 2U - 1U;
101 
102   timerCmp = (static_cast<uint16_t>(masterConfig->dataMode) * 2U - 1U) << 8U;
103   timerCmp |= timerDiv;
104 
105   timerConfig.timerCompare = timerCmp;
106 
107   FLEXIO_SetTimerConfig(flexio_spi_config_.flexioBase,
108                         flexio_spi_config_.timerIndex[0],
109                         &timerConfig);
110 }
111 
SpiCallback(FLEXIO_SPI_Type *,flexio_spi_master_handle_t *,status_t status,void * context)112 void McuxpressoFlexIoInitiator::SpiCallback(FLEXIO_SPI_Type*,
113                                             flexio_spi_master_handle_t*,
114                                             status_t status,
115                                             void* context) {
116   auto* driver = static_cast<McuxpressoFlexIoInitiator*>(context);
117   driver->last_transfer_status_ = ToPwStatus(status);
118   driver->transfer_semaphore_.release();
119 }
120 
DoConfigure(const Config & config)121 Status McuxpressoFlexIoInitiator::DoConfigure(const Config& config) {
122   if (current_config_ && config == *current_config_) {
123     return OkStatus();
124   }
125 
126   flexio_spi_master_config_t master_config = {};
127   FLEXIO_SPI_MasterGetDefaultConfig(&master_config);
128 
129   RESET_ClearPeripheralReset(kFLEXIO_RST_SHIFT_RSTn);
130 
131   if (config.phase == ClockPhase::kRisingEdge) {
132     master_config.phase = kFLEXIO_SPI_ClockPhaseFirstEdge;
133   } else {
134     master_config.phase = kFLEXIO_SPI_ClockPhaseSecondEdge;
135   }
136 
137   master_config.enableMaster = true;
138   master_config.baudRate_Bps = baud_rate_bps_;
139 
140   switch (config.bits_per_word()) {
141     case 8:
142       master_config.dataMode = kFLEXIO_SPI_8BitMode;
143       if (config.bit_order == BitOrder::kMsbFirst) {
144         transfer_flags_ = kFLEXIO_SPI_8bitMsb;
145       } else {
146         transfer_flags_ = kFLEXIO_SPI_8bitLsb;
147       }
148       break;
149     case 16:
150       master_config.dataMode = kFLEXIO_SPI_16BitMode;
151       if (config.bit_order == BitOrder::kMsbFirst) {
152         transfer_flags_ = kFLEXIO_SPI_16bitMsb;
153       } else {
154         transfer_flags_ = kFLEXIO_SPI_16bitLsb;
155       }
156       break;
157     default:
158       return Status::InvalidArgument();
159   }
160 
161   std::lock_guard lock(mutex_);
162   FLEXIO_SPI_MasterInit(&flexio_spi_config_, &master_config, src_clock_hz_);
163   ConfigureClock(&master_config, config.polarity);
164 
165   const auto status = ToPwStatus(FLEXIO_SPI_MasterTransferCreateHandle(
166       &flexio_spi_config_,
167       &driver_handle_,
168       McuxpressoFlexIoInitiator::SpiCallback,
169       this));
170 
171   if (status == OkStatus()) {
172     current_config_.emplace(config);
173   }
174   return status;
175 }
176 
DoWriteRead(ConstByteSpan write_buffer,ByteSpan read_buffer)177 Status McuxpressoFlexIoInitiator::DoWriteRead(ConstByteSpan write_buffer,
178                                               ByteSpan read_buffer) {
179   flexio_spi_transfer_t transfer = {};
180   transfer.txData =
181       reinterpret_cast<uint8_t*>(const_cast<std::byte*>(write_buffer.data()));
182   transfer.rxData = reinterpret_cast<uint8_t*>(read_buffer.data());
183   if (write_buffer.data() == nullptr && read_buffer.data() != nullptr) {
184     // Read only transaction
185     transfer.dataSize = read_buffer.size();
186   } else if (read_buffer.data() == nullptr && write_buffer.data() != nullptr) {
187     // Write only transaction
188     transfer.dataSize = write_buffer.size();
189   } else {
190     // Take smallest as size of transaction
191     transfer.dataSize = write_buffer.size() < read_buffer.size()
192                             ? write_buffer.size()
193                             : read_buffer.size();
194   }
195   transfer.flags = transfer_flags_;
196 
197   std::lock_guard lock(mutex_);
198   if (!current_config_) {
199     PW_LOG_ERROR("Mcuxpresso FlexIO_SPI must be configured before use.");
200     return Status::FailedPrecondition();
201   }
202   if (blocking_) {
203     return ToPwStatus(
204         FLEXIO_SPI_MasterTransferBlocking(&flexio_spi_config_, &transfer));
205   }
206 
207   PW_TRY(ToPwStatus(FLEXIO_SPI_MasterTransferNonBlocking(
208       &flexio_spi_config_, &driver_handle_, &transfer)));
209 
210   if (!transfer_semaphore_.try_acquire_for(kMaxWait)) {
211     return Status::DeadlineExceeded();
212   }
213   return last_transfer_status_;
214 }
215 
SetActive(bool active)216 Status McuxpressoFlexIoChipSelector::SetActive(bool active) {
217   return pin_.SetState(active ? digital_io::State::kInactive
218                               : digital_io::State::kActive);
219 }
220 // inclusive-language: enable
221 
222 }  // namespace pw::spi
223