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