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