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_rp2040/initiator.h"
16
17 #include <algorithm>
18
19 #include "hardware/spi.h"
20 #include "pico/stdlib.h"
21 #include "pw_assert/check.h"
22 #include "pw_log/log.h"
23 #include "pw_status/try.h"
24
25 namespace pw::spi {
26
27 namespace {
28
GetBitOrder(BitOrder bit_order)29 constexpr spi_order_t GetBitOrder(BitOrder bit_order) {
30 switch (bit_order) {
31 case BitOrder::kLsbFirst:
32 return SPI_LSB_FIRST;
33 case BitOrder::kMsbFirst:
34 return SPI_MSB_FIRST;
35 default:
36 PW_CRASH("Unknown bit order");
37 }
38 }
39
GetPhase(ClockPhase phase)40 constexpr spi_cpha_t GetPhase(ClockPhase phase) {
41 switch (phase) {
42 case ClockPhase::kRisingEdge:
43 return SPI_CPHA_0;
44 case ClockPhase::kFallingEdge:
45 return SPI_CPHA_1;
46 default:
47 PW_CRASH("Unknown phase");
48 }
49 }
50
GetPolarity(ClockPolarity polarity)51 constexpr spi_cpol_t GetPolarity(ClockPolarity polarity) {
52 switch (polarity) {
53 case ClockPolarity::kActiveHigh:
54 return SPI_CPOL_0;
55 case ClockPolarity::kActiveLow:
56 return SPI_CPOL_1;
57 default:
58 PW_CRASH("Unknown polarity");
59 }
60 }
61
62 } // namespace
63
Rp2040Initiator(spi_inst_t * spi)64 Rp2040Initiator::Rp2040Initiator(spi_inst_t* spi)
65 : spi_(spi), bits_per_word_(8) {}
66
LazyInit()67 Status Rp2040Initiator::LazyInit() {
68 // Already initialized - nothing to do.
69 // The Pico SDK needs to call spi_init() earlier so that the
70 // various GPIO pins (MISO, etc.) can be assigned to the SPI
71 // bus.
72 return OkStatus();
73 }
74
Configure(const Config & config)75 Status Rp2040Initiator::Configure(const Config& config) {
76 bits_per_word_ = config.bits_per_word;
77 PW_ASSERT(bits_per_word_() == 8);
78
79 spi_set_format(spi_,
80 config.bits_per_word(),
81 GetPolarity(config.polarity),
82 GetPhase(config.phase),
83 GetBitOrder(config.bit_order));
84
85 return OkStatus();
86 }
87
WriteRead(ConstByteSpan write_buffer,ByteSpan read_buffer)88 Status Rp2040Initiator::WriteRead(ConstByteSpan write_buffer,
89 ByteSpan read_buffer) {
90 if (write_buffer.empty() && !read_buffer.empty()) {
91 // Read only transaction.
92 spi_read_blocking(spi_,
93 /*repeated_tx_data=*/0,
94 reinterpret_cast<uint8_t*>(read_buffer.data()),
95 read_buffer.size());
96 } else if (!write_buffer.empty() && read_buffer.empty()) {
97 // Write only transaction.
98 spi_write_blocking(spi_,
99 reinterpret_cast<const uint8_t*>(write_buffer.data()),
100 write_buffer.size());
101 } else if (!write_buffer.empty() && !read_buffer.empty()) {
102 // Write & read transaction.
103 // Take the smallest as the size of transaction
104 auto transfer_size = write_buffer.size() < read_buffer.size()
105 ? write_buffer.size()
106 : read_buffer.size();
107 spi_write_read_blocking(
108 spi_,
109 reinterpret_cast<const uint8_t*>(write_buffer.data()),
110 reinterpret_cast<uint8_t*>(read_buffer.data()),
111 transfer_size);
112 } else {
113 return pw::Status::OutOfRange();
114 }
115
116 return OkStatus();
117 }
118
119 } // namespace pw::spi
120