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