xref: /aosp_15_r20/external/pigweed/pw_spi_linux/spi.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2021 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_linux/spi.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <fcntl.h>
18*61c4878aSAndroid Build Coastguard Worker #include <linux/spi/spidev.h>
19*61c4878aSAndroid Build Coastguard Worker #include <linux/types.h>
20*61c4878aSAndroid Build Coastguard Worker #include <sys/ioctl.h>
21*61c4878aSAndroid Build Coastguard Worker #include <unistd.h>
22*61c4878aSAndroid Build Coastguard Worker 
23*61c4878aSAndroid Build Coastguard Worker #include <cstring>
24*61c4878aSAndroid Build Coastguard Worker 
25*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.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 
~LinuxInitiator()30*61c4878aSAndroid Build Coastguard Worker LinuxInitiator::~LinuxInitiator() {
31*61c4878aSAndroid Build Coastguard Worker   if (fd_ >= 0) {
32*61c4878aSAndroid Build Coastguard Worker     close(fd_);
33*61c4878aSAndroid Build Coastguard Worker   }
34*61c4878aSAndroid Build Coastguard Worker }
35*61c4878aSAndroid Build Coastguard Worker 
DoConfigure(const Config & config)36*61c4878aSAndroid Build Coastguard Worker Status LinuxInitiator::DoConfigure(const Config& config) {
37*61c4878aSAndroid Build Coastguard Worker   if (current_config_ == config) {
38*61c4878aSAndroid Build Coastguard Worker     // Don't waste time issuing ioctls if the config is not actually changing.
39*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
40*61c4878aSAndroid Build Coastguard Worker   }
41*61c4878aSAndroid Build Coastguard Worker 
42*61c4878aSAndroid Build Coastguard Worker   // Map clock polarity/phase to Linux userspace equivalents
43*61c4878aSAndroid Build Coastguard Worker   uint32_t mode = 0;
44*61c4878aSAndroid Build Coastguard Worker   if (config.polarity == ClockPolarity::kActiveLow) {
45*61c4878aSAndroid Build Coastguard Worker     mode |= SPI_CPOL;  // Clock polarity -- signal is high when idle
46*61c4878aSAndroid Build Coastguard Worker   }
47*61c4878aSAndroid Build Coastguard Worker   if (config.phase == ClockPhase::kFallingEdge) {
48*61c4878aSAndroid Build Coastguard Worker     mode |= SPI_CPHA;  // Clock phase -- latch on falling edge
49*61c4878aSAndroid Build Coastguard Worker   }
50*61c4878aSAndroid Build Coastguard Worker   if (ioctl(fd_, SPI_IOC_WR_MODE32, &mode) < 0) {
51*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Unable to set SPI mode");
52*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
53*61c4878aSAndroid Build Coastguard Worker   }
54*61c4878aSAndroid Build Coastguard Worker 
55*61c4878aSAndroid Build Coastguard Worker   // Configure LSB/MSB first
56*61c4878aSAndroid Build Coastguard Worker   uint8_t lsb_first = 0;
57*61c4878aSAndroid Build Coastguard Worker   if (config.bit_order == BitOrder::kLsbFirst) {
58*61c4878aSAndroid Build Coastguard Worker     lsb_first = 1;  // non-zero value indicates LSB first
59*61c4878aSAndroid Build Coastguard Worker   }
60*61c4878aSAndroid Build Coastguard Worker   if (ioctl(fd_, SPI_IOC_WR_LSB_FIRST, &lsb_first) < 0) {
61*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Unable to set SPI LSB");
62*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
63*61c4878aSAndroid Build Coastguard Worker   }
64*61c4878aSAndroid Build Coastguard Worker 
65*61c4878aSAndroid Build Coastguard Worker   // Configure bits-per-word
66*61c4878aSAndroid Build Coastguard Worker   uint8_t bits_per_word = config.bits_per_word();
67*61c4878aSAndroid Build Coastguard Worker   if (ioctl(fd_, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word) < 0) {
68*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Unable to set SPI Bits Per Word");
69*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
70*61c4878aSAndroid Build Coastguard Worker   }
71*61c4878aSAndroid Build Coastguard Worker 
72*61c4878aSAndroid Build Coastguard Worker   // Configure maximum bus speed
73*61c4878aSAndroid Build Coastguard Worker   if (ioctl(fd_, SPI_IOC_WR_MAX_SPEED_HZ, &max_speed_hz_) < 0) {
74*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Unable to set SPI Max Speed");
75*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
76*61c4878aSAndroid Build Coastguard Worker   }
77*61c4878aSAndroid Build Coastguard Worker 
78*61c4878aSAndroid Build Coastguard Worker   current_config_ = config;
79*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
80*61c4878aSAndroid Build Coastguard Worker }
81*61c4878aSAndroid Build Coastguard Worker 
DoWriteRead(ConstByteSpan write_buffer,ByteSpan read_buffer)82*61c4878aSAndroid Build Coastguard Worker Status LinuxInitiator::DoWriteRead(ConstByteSpan write_buffer,
83*61c4878aSAndroid Build Coastguard Worker                                    ByteSpan read_buffer) {
84*61c4878aSAndroid Build Coastguard Worker   // Configure a full-duplex transfer using ioctl()
85*61c4878aSAndroid Build Coastguard Worker 
86*61c4878aSAndroid Build Coastguard Worker   // Neither read nor write: invalid.
87*61c4878aSAndroid Build Coastguard Worker   if (read_buffer.empty() && write_buffer.empty()) {
88*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
89*61c4878aSAndroid Build Coastguard Worker   }
90*61c4878aSAndroid Build Coastguard Worker 
91*61c4878aSAndroid Build Coastguard Worker   struct spi_ioc_transfer transaction[2] = {};
92*61c4878aSAndroid Build Coastguard Worker   unsigned long request = SPI_IOC_MESSAGE(1);  // macro arg must be constant
93*61c4878aSAndroid Build Coastguard Worker 
94*61c4878aSAndroid Build Coastguard Worker   if (read_buffer.empty()) {
95*61c4878aSAndroid Build Coastguard Worker     // Write-only.
96*61c4878aSAndroid Build Coastguard Worker     transaction[0].tx_buf = reinterpret_cast<uintptr_t>(write_buffer.data());
97*61c4878aSAndroid Build Coastguard Worker     transaction[0].len = write_buffer.size();
98*61c4878aSAndroid Build Coastguard Worker   } else if (write_buffer.empty()) {
99*61c4878aSAndroid Build Coastguard Worker     // Read-only.
100*61c4878aSAndroid Build Coastguard Worker     transaction[0].rx_buf = reinterpret_cast<uintptr_t>(read_buffer.data());
101*61c4878aSAndroid Build Coastguard Worker     transaction[0].len = read_buffer.size();
102*61c4878aSAndroid Build Coastguard Worker   } else {
103*61c4878aSAndroid Build Coastguard Worker     // Read+Write.
104*61c4878aSAndroid Build Coastguard Worker 
105*61c4878aSAndroid Build Coastguard Worker     // The common read+write amount.
106*61c4878aSAndroid Build Coastguard Worker     const size_t common_len = std::min(write_buffer.size(), read_buffer.size());
107*61c4878aSAndroid Build Coastguard Worker     transaction[0].tx_buf = reinterpret_cast<uintptr_t>(write_buffer.data());
108*61c4878aSAndroid Build Coastguard Worker     transaction[0].rx_buf = reinterpret_cast<uintptr_t>(read_buffer.data());
109*61c4878aSAndroid Build Coastguard Worker     transaction[0].len = common_len;
110*61c4878aSAndroid Build Coastguard Worker 
111*61c4878aSAndroid Build Coastguard Worker     // Handle different-sized buffers with a compound transaction
112*61c4878aSAndroid Build Coastguard Worker     if (write_buffer.size() > common_len) {
113*61c4878aSAndroid Build Coastguard Worker       auto write_remainder = write_buffer.subspan(common_len);
114*61c4878aSAndroid Build Coastguard Worker       transaction[1].tx_buf =
115*61c4878aSAndroid Build Coastguard Worker           reinterpret_cast<uintptr_t>(write_remainder.data());
116*61c4878aSAndroid Build Coastguard Worker       transaction[1].len = write_remainder.size();
117*61c4878aSAndroid Build Coastguard Worker       request = SPI_IOC_MESSAGE(2);
118*61c4878aSAndroid Build Coastguard Worker     } else if (read_buffer.size() > common_len) {
119*61c4878aSAndroid Build Coastguard Worker       auto read_remainder = read_buffer.subspan(common_len);
120*61c4878aSAndroid Build Coastguard Worker       transaction[1].rx_buf =
121*61c4878aSAndroid Build Coastguard Worker           reinterpret_cast<uintptr_t>(read_remainder.data());
122*61c4878aSAndroid Build Coastguard Worker       transaction[1].len = read_remainder.size();
123*61c4878aSAndroid Build Coastguard Worker       request = SPI_IOC_MESSAGE(2);
124*61c4878aSAndroid Build Coastguard Worker     }
125*61c4878aSAndroid Build Coastguard Worker   }
126*61c4878aSAndroid Build Coastguard Worker 
127*61c4878aSAndroid Build Coastguard Worker   if (ioctl(fd_, request, transaction) < 0) {
128*61c4878aSAndroid Build Coastguard Worker     PW_LOG_ERROR("Unable to perform SPI transfer");
129*61c4878aSAndroid Build Coastguard Worker     return Status::Unknown();
130*61c4878aSAndroid Build Coastguard Worker   }
131*61c4878aSAndroid Build Coastguard Worker 
132*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
133*61c4878aSAndroid Build Coastguard Worker }
134*61c4878aSAndroid Build Coastguard Worker 
SetActive(bool)135*61c4878aSAndroid Build Coastguard Worker Status LinuxChipSelector::SetActive(bool /*active*/) {
136*61c4878aSAndroid Build Coastguard Worker   // Note: For Linux' SPI userspace support, chip-select control is not exposed
137*61c4878aSAndroid Build Coastguard Worker   // directly to the user.  This limits our ability to use the SPI HAL to do
138*61c4878aSAndroid Build Coastguard Worker   // composite (multi read-write) transactions with the PW SPI HAL, as Linux
139*61c4878aSAndroid Build Coastguard Worker   // performs composite transactions with a single ioctl() call using an array
140*61c4878aSAndroid Build Coastguard Worker   // of descriptors provided as a parameter -- there's no way of separating
141*61c4878aSAndroid Build Coastguard Worker   // individual operations from userspace.  This could be addressed with a
142*61c4878aSAndroid Build Coastguard Worker   // direct "Composite" transaction HAL API, or by using a raw GPIO
143*61c4878aSAndroid Build Coastguard Worker   // to control of chip select from userspace (which is not common practice).
144*61c4878aSAndroid Build Coastguard Worker   return OkStatus();
145*61c4878aSAndroid Build Coastguard Worker }
146*61c4878aSAndroid Build Coastguard Worker 
147*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::spi
148