CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
platform-i2c.hh
1#pragma once
2#include <cdefs.h>
3#include <debug.hh>
4#include <stdint.h>
5
6/**
7 * The interrupts of the OpenTitan's I2C block.
8 *
9 * Documentation source can be found at:
10 * https://github.com/lowRISC/opentitan/blob/9ddf276c64e2974ed8e528e8b2feb00b977861de/hw/ip/i2c/doc/interfaces.md
11 */
12enum class OpenTitanI2cInterrupt
13{
14 /**
15 * A host mode interrupt. This is asserted whilst the Format FIFO level is
16 * below the low threshold. This is a level status interrupt.
17 */
18 FormatThreshold,
19 /**
20 * A host mode interrupt. This is asserted whilst the Receive FIFO level is
21 * above the high threshold. This is a level status interrupt.
22 */
23 ReceiveThreshold,
24 /**
25 * A target mode interrupt. This is asserted whilst the Aquired FIFO level
26 * is above the high threshold. This is a level status interrupt.
27 */
28 AcquiredThreshold,
29 /**
30 * A host mode interrupt. This is raised if the Receive FIFO has overflowed.
31 */
32 ReceiveOverflow,
33 /**
34 * A host mode interrupt. This is raised if there is no ACK in response to
35 * an address or data.
36 */
37 Nak,
38 /**
39 * A host mode interrupt. This is raised if the SCL line drops early (not
40 * supported without clock synchronization).
41 */
42 SclInterference,
43 /**
44 * A host mode interrupt. This is raised if the SDA line goes low when host
45 * is trying to assert high.
46 */
47 SdaInterference,
48 /**
49 * A host mode interrupt. This is raised if target stretches the clock
50 * beyond the allowed timeout period.
51 */
52 StretchTimeout,
53 /**
54 * A host mode interrupt. This is raised if the target does not assert a
55 * constant value of SDA during transmission.
56 */
57 SdaUnstable,
58 /**
59 * A host and target mode interrupt. In host mode, raised if the host issues
60 * a repeated START or terminates the transaction by issuing STOP. In target
61 * mode, raised if the external host issues a STOP or repeated START.
62 */
63 CommandComplete,
64 /**
65 * A target mode interrupt. This is raised if the target is stretching
66 * clocks for a read command. This is a level status interrupt.
67 */
68 TransmitStretch,
69 /**
70 * A target mode interrupt. This is asserted whilst the Transmit FIFO level
71 * is below the low threshold. This is a level status interrupt.
72 */
73 TransmitThreshold,
74 /**
75 * A target mode interrupt. This is raised if the target is stretching
76 * clocks due to full Aquired FIFO or zero count in targetAckControl.NBYTES
77 * (if enabled). This is a level status interrupt.
78 */
79 AcquiredFull,
80 /**
81 * A target mode interrupt. This is raised if STOP is received without a
82 * preceding NACK during an external host read.
83 */
84 UnexpectedStop,
85 /**
86 * A target mode interrupt. This is raised if the host stops sending the
87 * clock during an ongoing transaction.
88 */
89 HostTimeout,
90};
91
92static constexpr uint32_t interrupt_bit(const OpenTitanI2cInterrupt Interrupt)
93{
94 return 1 << static_cast<uint32_t>(Interrupt);
95};
96
97/**
98 * Driver for the OpenTitan's I2C block.
99 *
100 * Documentation source can be found at:
101 * https://github.com/lowRISC/opentitan/tree/9ddf276c64e2974ed8e528e8b2feb00b977861de/hw/ip/i2c
102 */
103struct OpenTitanI2c
104{
105 /// Interrupt State Register
106 uint32_t interruptState;
107 /// Interrupt Enable Register
108 uint32_t interruptEnable;
109 /// Interrupt Test Register
110 uint32_t interruptTest;
111 /// Alert Test Register (Unused in Sonata)
112 uint32_t alertTest;
113 /// I2C Control Register
114 uint32_t control;
115 /// I2C Live Status Register for Host and Target modes
116 uint32_t status;
117 /// I2C Read Data
118 uint32_t readData;
119 /// I2C Host Format Data
120 uint32_t formatData;
121 /// I2C FIFO control register
122 uint32_t fifoCtrl;
123 /// Host mode FIFO configuration
124 uint32_t hostFifoConfiguration;
125 /// Target mode FIFO configuration
127 /// Host mode FIFO status register
128 uint32_t hostFifoStatus;
129 /// Target mode FIFO status register
130 uint32_t targetFifoStatus;
131 /// I2C Override Control Register
132 uint32_t override;
133 /// Oversampled Receive values
134 uint32_t values;
135 /**
136 * Detailed I2C Timings (directly corresponding to table 10 in the I2C
137 * Specification).
138 */
139 uint32_t timing[5];
140 /// I2C clock stretching timeout control.
141 uint32_t timeoutControl;
142 /// I2C target address and mask pairs
143 uint32_t targetId;
144 /// I2C target acquired data
145 uint32_t acquiredData;
146 /// I2C target transmit data
147 uint32_t transmitData;
148 /**
149 * I2C host clock generation timeout value (in units of input clock
150 * frequency).
151 */
152 uint32_t hostTimeoutControl;
153 /// I2C target internal stretching timeout control.
154 uint32_t targetTimeoutControl;
155 /**
156 * Number of times the I2C target has NACK'ed a new transaction since the
157 * last read of this register.
158 */
159 uint32_t targetNackCount;
160 /**
161 * Timeout in Host-Mode for an unhandled NACK before hardware automatically
162 * ends the transaction.
163 */
164 uint32_t targetAckControl;
165
166 /// Control Register Fields
167 enum [[clang::flag_enum]] : uint32_t{
168 /// Enable Host I2C functionality
169 ControlEnableHost = 1 << 0,
170 /// Enable Target I2C functionality
171 ControlEnableTarget = 1 << 1,
172 /// Enable I2C line loopback test If line loopback is enabled, the
173 /// internal design sees ACQ and RX data as "1"
174 ControlLineLoopback = 1 << 2,
175 };
176
177 /// Status Register Fields
178 enum [[clang::flag_enum]] : uint32_t{
179 /// Host mode Format FIFO is full
180 StatusFormatFull = 1 << 0,
181 /// Host mode Receive FIFO is full
182 StatusReceiveFull = 1 << 1,
183 /// Host mode Format FIFO is empty
184 StatusFormatEmpty = 1 << 2,
185 /// Host functionality is idle. No Host transaction is in progress
186 StatusHostIdle = 1 << 3,
187 /// Target functionality is idle. No Target transaction is in progress
188 StatusTargetIdle = 1 << 4,
189 /// Host mode Receive FIFO is empty
190 SmatusReceiveEmpty = 1 << 5,
191 /// Target mode Transmit FIFO is full
192 StatusTransmitFull = 1 << 6,
193 /// Target mode Receive FIFO is full
194 StatusAcquiredFull = 1 << 7,
195 /// Target mode Transmit FIFO is empty
196 StatusTransmitEmpty = 1 << 8,
197 /// Target mode Aquired FIFO is empty
198 StatusAcquiredEmpty = 1 << 9,
199 /**
200 * A Host-Mode active transaction has been ended by the
201 * HostNackHandlerTimeout mechanism. This bit is cleared when
202 * Control.EnableHost is set by software to start a new transaction.
203 */
205 };
206
207 /// FormatData Register Fields
208 enum [[clang::flag_enum]] : uint32_t{
209 /// Issue a START condition before transmitting BYTE.
210 FormatDataStart = 1 << 8,
211 /// Issue a STOP condition after this operation
212 FormatDataStop = 1 << 9,
213 /// Read BYTE bytes from I2C. (256 if BYTE==0)
214 FormatDataReadBytes = 1 << 10,
215 /**
216 * Do not NACK the last byte read, let the read
217 * operation continue
218 */
219 FormatDataReadCount = 1 << 11,
220 /// Do not signal an exception if the current byte is not ACK’d
221 FormatDataNakOk = 1 << 12,
222 };
223
224 /// FifoControl Register Fields
225 enum [[clang::flag_enum]] : uint32_t{
226 /// Receive fifo reset. Write 1 to the register resets it. Read returns 0
228 /// Format fifo reset. Write 1 to the register resets it. Read returns 0
229 FifoControlFormatReset = 1 << 1,
230 /// Aquired FIFO reset. Write 1 to the register resets it. Read returns 0
232 /// Transmit FIFO reset. Write 1 to the register resets it. Read returns 0
234 };
235
236 /// Flag set when we're debugging this driver.
237 static constexpr bool DebugOpenTitanI2c = true;
238
239 /// Helper for conditional debug logs and assertions.
240 using Debug = ConditionalDebug<DebugOpenTitanI2c, "OpenTitan I2C">;
241
242 /**
243 * Performs a 32-bit integer unsigned division, rounding up. The bottom
244 * 16 bits of the result are then returned.
245 *
246 * As usual, a divisor of 0 is still Undefined Behavior.
247 */
248 static uint16_t round_up_divide(uint32_t a, uint32_t b)
249 {
250 if (a == 0)
251 {
252 return 0;
253 }
254 const uint32_t Res = ((a - 1) / b) + 1;
255 Debug::Assert(Res <= UINT16_MAX,
256 "Division result too large to fit in uint16_t.");
257 return static_cast<uint16_t>(Res);
258 }
259
260 /// Reset all of the fifos.
266
267 /// Configure the I2C block to be in host mode.
268 void host_mode_set() volatile
269 {
271 }
272
273 /**
274 * Set the I2C timing parameters appropriately for the given bit rate.
275 * Distilled from:
276 * https://github.com/lowRISC/opentitan/blob/9ddf276c64e2974ed8e528e8b2feb00b977861de/hw/ip/i2c/doc/programmers_guide.md
277 */
278 void speed_set(const uint32_t SpeedKhz) volatile
279 {
280 // We must round up the system clock frequency to lengthen intervals.
281 const uint16_t SystemClockKhz = round_up_divide(CPU_TIMER_HZ, 1000);
282 // We want to underestimate the clock period, to lengthen the timings.
283 const uint16_t ClockPeriod = (1000 * 1000) / SystemClockKhz;
284
285 // Decide which bus mode this represents
286 uint32_t mode = (SpeedKhz > 100u) + (SpeedKhz > 400u);
287
288 // Minimum fall time when V_DD is 3.3V
289 constexpr uint16_t MinimumFallTime = 20 * 3 / 5;
290 // Specification minimum timings (Table 10) in nanoseconds for each bus
291 // mode.
292 constexpr uint16_t MinimumTimeValues[5][2][3] = {
293 {
294 {4700u, 1300u, 150u}, // Low Period
295 {4000u, 600u, 260u}, // High Period
296 },
297 {
298 // Fall time of SDA and SCL signals
299 {MinimumFallTime, MinimumFallTime, MinimumFallTime},
300 // Rise time of SDA and SCL signals
301 {120, 120, 120},
302 },
303 {
304 {4700u, 600u, 260u}, // Hold time for a repeated start condition
305 {4000u, 600u, 260u}, // Set-up time for a repeated start condition
306 },
307 {
308 {4000u, 1u, 1u}, // Data hold time
309 {500u, 100u, 50u}, // Data set-up time
310 },
311 {
312 // Bus free time between a STOP and START condition
313 {4700u, 1300u, 500u},
314 // Set-up time for a STOP condition
315 {4000u, 600u, 260u},
316 },
317 };
318 for (uint32_t i = 0; i < 5; ++i)
319 {
320 timing[i] =
321 (round_up_divide(MinimumTimeValues[i][0][mode], ClockPeriod)
322 << 16) |
323 round_up_divide(MinimumTimeValues[i][1][mode], ClockPeriod);
324 }
325 }
326
327 void blocking_write_byte(const uint32_t Fmt) volatile
328 {
329 while (0 != (StatusFormatFull & status)) {}
330 formatData = Fmt;
331 }
332
333 /// Returns true when the format fifo is empty
334 [[nodiscard]] bool format_is_empty() volatile
335 {
336 return 0 != (StatusFormatEmpty & status);
337 }
338
339 void blocking_write(const uint8_t Addr7,
340 const uint8_t data[],
341 const uint32_t NumBytes,
342 const bool SkipStop) volatile
343 {
344 if (NumBytes == 0)
345 {
346 return;
347 }
348 blocking_write_byte(FormatDataStart | (Addr7 << 1) | 0u);
349 for (uint32_t i = 0; i < NumBytes - 1; ++i)
350 {
351 blocking_write_byte(data[i]);
352 }
353 blocking_write_byte((SkipStop ? 0u : FormatDataStop) |
354 data[NumBytes - 1]);
355 }
356
357 [[nodiscard]] bool blocking_read(const uint8_t Addr7,
358 uint8_t buf[],
359 const uint32_t NumBytes) volatile
360 {
361 for (uint32_t idx = 0; idx < NumBytes; idx += UINT8_MAX)
362 {
363 blocking_write_byte(FormatDataStart | (Addr7 << 1) | 1u);
364 while (!format_is_empty()) {}
365 if (interrupt_is_asserted(OpenTitanI2cInterrupt::Nak))
366 {
367 interrupt_clear(OpenTitanI2cInterrupt::Nak);
368 return false;
369 }
370 uint32_t bytesRemaining = NumBytes - idx;
371 bool lastChunk = UINT8_MAX >= bytesRemaining;
372 uint8_t chunkSize =
373 lastChunk ? static_cast<uint8_t>(bytesRemaining) : UINT8_MAX;
374
375 blocking_write_byte((lastChunk ? FormatDataStop : 0) |
376 FormatDataReadBytes | chunkSize);
377 while (!format_is_empty()) {}
378
379 for (uint32_t chunkIdx = 0; chunkIdx < chunkSize; ++chunkIdx)
380 {
381 buf[idx + chunkIdx] = readData;
382 }
383 }
384 return true;
385 }
386
387 /// Returns true if the given interrupt is asserted.
388 [[nodiscard]] bool
389 interrupt_is_asserted(OpenTitanI2cInterrupt interrupt) volatile
390 {
391 return 0 != (interruptState & interrupt_bit(interrupt));
392 }
393
394 /// Clears the given interrupt.
395 void interrupt_clear(OpenTitanI2cInterrupt interrupt) volatile
396 {
397 interruptState = interrupt_bit(interrupt);
398 }
399
400 /// Enables the given interrupt.
401 void interrupt_enable(OpenTitanI2cInterrupt interrupt) volatile
402 {
403 interruptEnable = interruptEnable | interrupt_bit(interrupt);
404 }
405
406 /// Disables the given interrupt.
407 void interrupt_disable(OpenTitanI2cInterrupt interrupt) volatile
408 {
409 interruptEnable = interruptEnable & ~interrupt_bit(interrupt);
410 }
411
412 /**
413 * Sets the thresholds for the format and receive fifos.
414 */
415 void host_thresholds_set(uint16_t formatThreshold,
416 uint16_t receiveThreshold) volatile
417 {
419 (formatThreshold & 0xfff) << 16 | (receiveThreshold & 0xfff);
420 }
421};
C++ APIs for assertions, invariants, and writing formatted debug messages to a UART.
Driver for the OpenTitan's I2C block.
uint32_t status
I2C Live Status Register for Host and Target modes.
uint32_t readData
I2C Read Data.
@ FormatDataNakOk
Do not signal an exception if the current byte is not ACK’d.
@ FormatDataStop
Issue a STOP condition after this operation.
@ FormatDataStart
Issue a START condition before transmitting BYTE.
@ FormatDataReadCount
Do not NACK the last byte read, let the read operation continue.
@ FormatDataReadBytes
Read BYTE bytes from I2C. (256 if BYTE==0).
uint32_t interruptState
Interrupt State Register.
uint32_t values
Oversampled Receive values.
uint32_t targetNackCount
Number of times the I2C target has NACK'ed a new transaction since the last read of this register.
void host_thresholds_set(uint16_t formatThreshold, uint16_t receiveThreshold) volatile
Sets the thresholds for the format and receive fifos.
uint32_t targetAckControl
Controls for mid-transfer (N)ACK phase handling.
uint32_t targetFifoConfiguration
Target mode FIFO configuration.
bool interrupt_is_asserted(OpenTitanI2cInterrupt interrupt) volatile
Returns true if the given interrupt is asserted.
uint32_t timing[5]
Detailed I2C Timings (directly corresponding to table 10 in the I2C Specification).
uint32_t formatData
I2C Host Format Data.
void interrupt_enable(OpenTitanI2cInterrupt interrupt) volatile
Enables the given interrupt.
uint32_t fifoCtrl
I2C FIFO control register.
void reset_fifos() volatile
Reset all of the fifos.
void interrupt_disable(OpenTitanI2cInterrupt interrupt) volatile
Disables the given interrupt.
@ ControlLineLoopback
Enable I2C line loopback test If line loopback is enabled, the internal design sees ACQ and RX data a...
@ ControlEnableTarget
Enable Target I2C functionality.
@ ControlEnableHost
Enable Host I2C functionality.
uint32_t hostFifoConfiguration
Host mode FIFO configuration.
uint32_t targetFifoStatus
Target mode FIFO status register.
bool format_is_empty() volatile
Returns true when the format fifo is empty.
static constexpr bool DebugOpenTitanI2c
Flag set when we're debugging this driver.
uint32_t acquiredData
I2C target acquired data.
uint32_t targetTimeoutControl
I2C target internal stretching timeout control.
@ FifoControlFormatReset
Format fifo reset. Write 1 to the register resets it. Read returns 0.
@ FifoControlTransmitReset
Transmit FIFO reset.
@ FifoControlReceiveReset
Receive fifo reset.
@ FifoControlAcquiredReset
Acquired FIFO reset.
static uint16_t round_up_divide(uint32_t a, uint32_t b)
Performs a 32-bit integer unsigned division, rounding up.
uint32_t timeoutControl
I2C clock stretching timeout control.
uint32_t hostFifoStatus
Host mode FIFO status register.
uint32_t control
I2C Control Register.
uint32_t hostTimeoutControl
I2C host clock generation timeout value (in units of input clock frequency).
@ StatusTransmitFull
Target mode Transmit FIFO is full.
@ StatusHostDisabledNackTimeout
A Host-Mode active transaction has been ended by the HostNackHandlerTimeout mechanism.
@ StatusReceiveFull
Host mode Receive FIFO is full.
@ StatusAcquiredFull
Target mode Acquired FIFO is full.
@ StatusFormatEmpty
Host mode Format FIFO is empty.
@ StatusTransmitEmpty
Target mode Transmit FIFO is empty.
@ StatusHostIdle
Host functionality is idle. No Host transaction is in progress.
@ StatusTargetIdle
Target functionality is idle. No Target transaction is in progress.
@ StatusAcquiredEmpty
Target mode Acquired FIFO is empty.
@ StatusFormatFull
Host mode Format FIFO is full.
@ SmatusReceiveEmpty
Host mode Receive FIFO is empty.
uint32_t interruptTest
Interrupt Test Register.
void speed_set(const uint32_t SpeedKhz) volatile
Set the I2C timing parameters appropriately for the given bit rate.
uint32_t alertTest
Alert Test Register (Unused in Sonata).
uint32_t transmitData
I2C target transmit data.
bool interrupt_is_asserted(Interrupt interrupt) volatile
Returns true if the given interrupt is asserted.
void interrupt_clear(Interrupt interrupt) volatile
Clears the given interrupt.
uint32_t interruptEnable
Interrupt Enable Register.
uint32_t targetId
I2C target address and mask pairs.
void host_mode_set() volatile
Configure the I2C block to be in host mode.
ConditionalDebug< DebugOpenTitanI2c, "OpenTitan I2C"> Debug
Helper for conditional debug logs and assertions.
void interrupt_clear(OpenTitanI2cInterrupt interrupt) volatile
Clears the given interrupt.