xref: /aosp_15_r20/external/pigweed/pw_i2c_rp2040/initiator.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 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 #include "pw_i2c_rp2040/initiator.h"
15*61c4878aSAndroid Build Coastguard Worker 
16*61c4878aSAndroid Build Coastguard Worker #include <chrono>
17*61c4878aSAndroid Build Coastguard Worker #include <mutex>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "hardware/gpio.h"
20*61c4878aSAndroid Build Coastguard Worker #include "hardware/i2c.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pico/error.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pico/types.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_chrono/system_clock.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
26*61c4878aSAndroid Build Coastguard Worker 
27*61c4878aSAndroid Build Coastguard Worker namespace pw::i2c {
28*61c4878aSAndroid Build Coastguard Worker 
29*61c4878aSAndroid Build Coastguard Worker namespace {
30*61c4878aSAndroid Build Coastguard Worker 
PicoStatusToPwStatus(int status)31*61c4878aSAndroid Build Coastguard Worker Status PicoStatusToPwStatus(int status) {
32*61c4878aSAndroid Build Coastguard Worker   if (status > 0)
33*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
34*61c4878aSAndroid Build Coastguard Worker 
35*61c4878aSAndroid Build Coastguard Worker   switch (status) {
36*61c4878aSAndroid Build Coastguard Worker     case PICO_ERROR_TIMEOUT:
37*61c4878aSAndroid Build Coastguard Worker       return Status::DeadlineExceeded();
38*61c4878aSAndroid Build Coastguard Worker     default:
39*61c4878aSAndroid Build Coastguard Worker       return Status::Unavailable();
40*61c4878aSAndroid Build Coastguard Worker   }
41*61c4878aSAndroid Build Coastguard Worker }
42*61c4878aSAndroid Build Coastguard Worker }  // namespace
43*61c4878aSAndroid Build Coastguard Worker 
Enable()44*61c4878aSAndroid Build Coastguard Worker void Rp2040Initiator::Enable() {
45*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(mutex_);
46*61c4878aSAndroid Build Coastguard Worker 
47*61c4878aSAndroid Build Coastguard Worker   i2c_init(instance_, config_.clock_frequency_hz);
48*61c4878aSAndroid Build Coastguard Worker   gpio_set_function(config_.sda_pin, GPIO_FUNC_I2C);
49*61c4878aSAndroid Build Coastguard Worker   gpio_set_function(config_.scl_pin, GPIO_FUNC_I2C);
50*61c4878aSAndroid Build Coastguard Worker 
51*61c4878aSAndroid Build Coastguard Worker   enabled_ = true;
52*61c4878aSAndroid Build Coastguard Worker }
53*61c4878aSAndroid Build Coastguard Worker 
Disable()54*61c4878aSAndroid Build Coastguard Worker void Rp2040Initiator::Disable() {
55*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(mutex_);
56*61c4878aSAndroid Build Coastguard Worker   i2c_deinit(instance_);
57*61c4878aSAndroid Build Coastguard Worker   enabled_ = false;
58*61c4878aSAndroid Build Coastguard Worker }
59*61c4878aSAndroid Build Coastguard Worker 
~Rp2040Initiator()60*61c4878aSAndroid Build Coastguard Worker Rp2040Initiator::~Rp2040Initiator() { Disable(); }
61*61c4878aSAndroid Build Coastguard Worker 
62*61c4878aSAndroid Build Coastguard Worker // Performs blocking I2C write, read and read-after-write depending on the tx
63*61c4878aSAndroid Build Coastguard Worker // and rx buffer states.
DoWriteReadFor(Address device_address,ConstByteSpan tx_buffer,ByteSpan rx_buffer,chrono::SystemClock::duration timeout)64*61c4878aSAndroid Build Coastguard Worker Status Rp2040Initiator::DoWriteReadFor(Address device_address,
65*61c4878aSAndroid Build Coastguard Worker                                        ConstByteSpan tx_buffer,
66*61c4878aSAndroid Build Coastguard Worker                                        ByteSpan rx_buffer,
67*61c4878aSAndroid Build Coastguard Worker                                        chrono::SystemClock::duration timeout) {
68*61c4878aSAndroid Build Coastguard Worker   if (timeout <= chrono::SystemClock::duration::zero()) {
69*61c4878aSAndroid Build Coastguard Worker     return Status::DeadlineExceeded();
70*61c4878aSAndroid Build Coastguard Worker   }
71*61c4878aSAndroid Build Coastguard Worker   // The number of us to wait plus + 1 to ensure we wait at least the full
72*61c4878aSAndroid Build Coastguard Worker   // duration. Ideally we would add one additional tick but the pico_sdk only
73*61c4878aSAndroid Build Coastguard Worker   // supports timeouts specified in us.
74*61c4878aSAndroid Build Coastguard Worker   const int64_t timeout_us = std::chrono::microseconds(timeout).count() + 1;
75*61c4878aSAndroid Build Coastguard Worker 
76*61c4878aSAndroid Build Coastguard Worker   if (timeout_us > std::numeric_limits<uint>::max()) {
77*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
78*61c4878aSAndroid Build Coastguard Worker   }
79*61c4878aSAndroid Build Coastguard Worker 
80*61c4878aSAndroid Build Coastguard Worker   const uint8_t address = device_address.GetSevenBit();
81*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(mutex_);
82*61c4878aSAndroid Build Coastguard Worker 
83*61c4878aSAndroid Build Coastguard Worker   if (!enabled_) {
84*61c4878aSAndroid Build Coastguard Worker     return Status::FailedPrecondition();
85*61c4878aSAndroid Build Coastguard Worker   }
86*61c4878aSAndroid Build Coastguard Worker 
87*61c4878aSAndroid Build Coastguard Worker   if (!tx_buffer.empty() && rx_buffer.empty()) {
88*61c4878aSAndroid Build Coastguard Worker     // Write
89*61c4878aSAndroid Build Coastguard Worker     int result = i2c_write_timeout_us(
90*61c4878aSAndroid Build Coastguard Worker         instance_,
91*61c4878aSAndroid Build Coastguard Worker         address,
92*61c4878aSAndroid Build Coastguard Worker         /*src=*/reinterpret_cast<const uint8_t*>(tx_buffer.data()),
93*61c4878aSAndroid Build Coastguard Worker         /*len=*/tx_buffer.size(),
94*61c4878aSAndroid Build Coastguard Worker         /*nostop=*/false,
95*61c4878aSAndroid Build Coastguard Worker         static_cast<uint>(timeout_us));
96*61c4878aSAndroid Build Coastguard Worker 
97*61c4878aSAndroid Build Coastguard Worker     return PicoStatusToPwStatus(result);
98*61c4878aSAndroid Build Coastguard Worker 
99*61c4878aSAndroid Build Coastguard Worker   } else if (tx_buffer.empty() && !rx_buffer.empty()) {
100*61c4878aSAndroid Build Coastguard Worker     // Read
101*61c4878aSAndroid Build Coastguard Worker     int result = i2c_read_timeout_us(
102*61c4878aSAndroid Build Coastguard Worker         instance_,
103*61c4878aSAndroid Build Coastguard Worker         address,
104*61c4878aSAndroid Build Coastguard Worker         /*src=*/reinterpret_cast<uint8_t*>(rx_buffer.data()),
105*61c4878aSAndroid Build Coastguard Worker         /*len=*/rx_buffer.size(),
106*61c4878aSAndroid Build Coastguard Worker         /*nostop=*/false,
107*61c4878aSAndroid Build Coastguard Worker         static_cast<uint>(timeout_us));
108*61c4878aSAndroid Build Coastguard Worker     return PicoStatusToPwStatus(result);
109*61c4878aSAndroid Build Coastguard Worker 
110*61c4878aSAndroid Build Coastguard Worker   } else if (!tx_buffer.empty() && !rx_buffer.empty()) {
111*61c4878aSAndroid Build Coastguard Worker     absolute_time_t timeout_absolute = make_timeout_time_us(timeout_us);
112*61c4878aSAndroid Build Coastguard Worker     // Write then Read
113*61c4878aSAndroid Build Coastguard Worker     int write_result = i2c_write_blocking_until(
114*61c4878aSAndroid Build Coastguard Worker         instance_,
115*61c4878aSAndroid Build Coastguard Worker         address,
116*61c4878aSAndroid Build Coastguard Worker         /*src=*/reinterpret_cast<const uint8_t*>(tx_buffer.data()),
117*61c4878aSAndroid Build Coastguard Worker         /*len=*/tx_buffer.size(),
118*61c4878aSAndroid Build Coastguard Worker         /*nostop=*/true,
119*61c4878aSAndroid Build Coastguard Worker         /*until=*/timeout_absolute);
120*61c4878aSAndroid Build Coastguard Worker     pw::Status write_status(PicoStatusToPwStatus(write_result));
121*61c4878aSAndroid Build Coastguard Worker 
122*61c4878aSAndroid Build Coastguard Worker     if (write_status != OkStatus()) {
123*61c4878aSAndroid Build Coastguard Worker       return write_status;
124*61c4878aSAndroid Build Coastguard Worker     }
125*61c4878aSAndroid Build Coastguard Worker 
126*61c4878aSAndroid Build Coastguard Worker     int read_result = i2c_read_blocking_until(
127*61c4878aSAndroid Build Coastguard Worker         instance_,
128*61c4878aSAndroid Build Coastguard Worker         address,
129*61c4878aSAndroid Build Coastguard Worker         /*src=*/reinterpret_cast<uint8_t*>(rx_buffer.data()),
130*61c4878aSAndroid Build Coastguard Worker         /*len=*/rx_buffer.size(),
131*61c4878aSAndroid Build Coastguard Worker         /*nostop=*/false,
132*61c4878aSAndroid Build Coastguard Worker         /*until=*/timeout_absolute);
133*61c4878aSAndroid Build Coastguard Worker 
134*61c4878aSAndroid Build Coastguard Worker     return PicoStatusToPwStatus(read_result);
135*61c4878aSAndroid Build Coastguard Worker 
136*61c4878aSAndroid Build Coastguard Worker   } else {
137*61c4878aSAndroid Build Coastguard Worker     return Status::InvalidArgument();
138*61c4878aSAndroid Build Coastguard Worker   }
139*61c4878aSAndroid Build Coastguard Worker }
140*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::i2c
141