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