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