ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_smbus.c
Go to the documentation of this file.
1
22
23#include "ra8_smbus.h"
24
25#include <stdint.h>
26
27#include "ra8_attributes.h"
28#include "ra8_check.h"
29#include "ra8_err.h"
30#include "ra8_i2c_bus_ops.h"
31#include "ra8_log.h"
32
34static const char* s_tag = "SMBUS";
35
37typedef enum : uint16_t {
41
56
71
74 .initialized = false,
75 .bus = {},
76 .pec_enabled = false,
77 .alert_fn = nullptr,
78 .alert_ctx = nullptr,
79};
80
81/* =============================================================================
82 * PEC: CRC-8 / SMBus
83 * =============================================================================
84 */
85
109RA8_INTERNAL static uint8_t internal_pec_update(uint8_t crc, uint8_t b)
110{
111 uint8_t c = (uint8_t)(crc ^ b);
112 for (uint8_t i = 0U; i < (uint8_t)k_ra8_smbus_msb_for_byte; ++i) {
113 if ((c & (uint8_t)k_ra8_smbus_pec_top_bit) != 0U) {
114 c = (uint8_t)((uint8_t)(c << 1U) ^ (uint8_t)k_ra8_smbus_pec_poly);
115 } else {
116 c = (uint8_t)(c << 1U);
117 }
118 }
119 return c;
120}
121
122uint8_t ra8_smbus_pec(const uint8_t* data, uint32_t len)
123{
124 if ((data == nullptr) || (len == 0U)) {
125 return (uint8_t)k_ra8_smbus_pec_init;
126 }
127 uint8_t c = (uint8_t)k_ra8_smbus_pec_init;
128 for (uint32_t i = 0U; i < len; ++i) {
129 c = internal_pec_update(c, data[i]);
130 }
131 return c;
132}
133
134/* =============================================================================
135 * Address-byte helpers
136 * =============================================================================
137 */
138
139/* Build the on-the-wire address byte for a 7-bit target -- see implementation for details. */
141RA8_INTERNAL static uint8_t internal_make_addr_byte(uint8_t target_7b, uint8_t rw_bit)
142{
143 return (uint8_t)(((uint32_t)target_7b << (uint32_t)k_ra8_smbus_addr_shift) |
144 ((uint32_t)rw_bit & 1U));
145}
146
147/* =============================================================================
148 * Lifecycle
149 * =============================================================================
150 */
151
153{
154 RA8_CHECK_NULL_PTR(cfg, s_tag, "smbus_init: cfg");
155 if (cfg->bus.write == nullptr) {
156 ra8_log_error(s_tag, "init: bus.write missing");
158 }
159 if (cfg->bus.read == nullptr) {
160 ra8_log_error(s_tag, "init: bus.read missing");
162 }
163 if (cfg->bus.transfer == nullptr) {
164 ra8_log_error(s_tag, "init: bus.transfer missing");
166 }
167 s_state.initialized = true;
168 s_state.bus = cfg->bus;
169 s_state.pec_enabled = cfg->pec_enabled;
170 s_state.alert_fn = nullptr;
171 s_state.alert_ctx = nullptr;
172 return k_ra8_ok;
173}
174
176{
177 if (!s_state.initialized) {
179 }
180 s_state.initialized = false;
181 s_state.alert_fn = nullptr;
182 s_state.alert_ctx = nullptr;
183 return k_ra8_ok;
184}
185
186/* =============================================================================
187 * Send Byte / Receive Byte (SMBus 3.2 sec 6.5.2 / 6.5.3)
188 * =============================================================================
189 */
190
191ra8_err_t ra8_smbus_send_byte(uint8_t target_7b, uint8_t data)
192{
193 if (!s_state.initialized) {
195 }
196 /* Frame: [data] (+ optional [PEC]) -- the address byte is emitted by
197 * the bus write itself but participates in the PEC. */
198 uint8_t buf[2];
199 uint32_t len = 0U;
200 buf[len++] = data;
201 if (s_state.pec_enabled) {
202 const uint8_t addr_b = internal_make_addr_byte(target_7b, (uint8_t)k_ra8_smbus_rw_write);
203 uint8_t pec = internal_pec_update((uint8_t)k_ra8_smbus_pec_init, addr_b);
204 pec = internal_pec_update(pec, data);
205 buf[len++] = pec;
206 }
207 return s_state.bus.write(s_state.bus.ctx, target_7b, buf, len, true);
208}
209
210ra8_err_t ra8_smbus_receive_byte(uint8_t target_7b, uint8_t* out_data)
211{
212 RA8_CHECK_NULL_PTR(out_data, s_tag, "receive_byte: out_data");
213 if (!s_state.initialized) {
215 }
216 uint8_t buf[2] = {0U, 0U};
217 const uint32_t len = s_state.pec_enabled ? 2U : 1U;
218 const ra8_err_t err = s_state.bus.read(s_state.bus.ctx, target_7b, buf, len);
219 if (err != k_ra8_ok) {
220 return err;
221 }
222 if (s_state.pec_enabled) {
223 const uint8_t addr_b = internal_make_addr_byte(target_7b, (uint8_t)k_ra8_smbus_rw_read);
224 uint8_t pec = internal_pec_update((uint8_t)k_ra8_smbus_pec_init, addr_b);
225 pec = internal_pec_update(pec, buf[0]);
226 if (pec != buf[1]) {
227 ra8_log_error(s_tag, "receive_byte: PEC mismatch");
229 }
230 }
231 *out_data = buf[0];
232 return k_ra8_ok;
233}
234
235/* =============================================================================
236 * Write Byte Data / Read Byte Data (SMBus 3.2 sec 6.5.4 / 6.5.5)
237 * =============================================================================
238 */
239
240ra8_err_t ra8_smbus_write_byte_data(uint8_t target_7b, uint8_t cmd, uint8_t data)
241{
242 if (!s_state.initialized) {
244 }
245 uint8_t buf[3];
246 uint32_t len = 0U;
247 buf[len++] = cmd;
248 buf[len++] = data;
249 if (s_state.pec_enabled) {
250 const uint8_t addr_b = internal_make_addr_byte(target_7b, (uint8_t)k_ra8_smbus_rw_write);
251 uint8_t pec = internal_pec_update((uint8_t)k_ra8_smbus_pec_init, addr_b);
252 pec = internal_pec_update(pec, cmd);
253 pec = internal_pec_update(pec, data);
254 buf[len++] = pec;
255 }
256 return s_state.bus.write(s_state.bus.ctx, target_7b, buf, len, true);
257}
258
259ra8_err_t ra8_smbus_read_byte_data(uint8_t target_7b, uint8_t cmd, uint8_t* out_data)
260{
261 RA8_CHECK_NULL_PTR(out_data, s_tag, "read_byte_data: out_data");
262 if (!s_state.initialized) {
264 }
265 /* Combined transfer: write [cmd], RESTART, read [data] (+ optional
266 * [PEC]). The bus transfer handles the repeated-START framing. */
267 uint8_t rx[2] = {0U, 0U};
268 const uint32_t rxlen = s_state.pec_enabled ? 2U : 1U;
269 const ra8_err_t err = s_state.bus.transfer(s_state.bus.ctx, target_7b, &cmd, 1U, rx, rxlen);
270 if (err != k_ra8_ok) {
271 return err;
272 }
273 if (s_state.pec_enabled) {
274 const uint8_t addr_w = internal_make_addr_byte(target_7b, (uint8_t)k_ra8_smbus_rw_write);
275 const uint8_t addr_r = internal_make_addr_byte(target_7b, (uint8_t)k_ra8_smbus_rw_read);
276 uint8_t pec = internal_pec_update((uint8_t)k_ra8_smbus_pec_init, addr_w);
277 pec = internal_pec_update(pec, cmd);
278 pec = internal_pec_update(pec, addr_r);
279 pec = internal_pec_update(pec, rx[0]);
280 if (pec != rx[1]) {
281 ra8_log_error(s_tag, "read_byte_data: PEC mismatch");
283 }
284 } /* GCOVR_EXCL_LINE -- reached only when PEC matches; host fake returns constant NTDTBP0 */
285 *out_data = rx[0];
286 return k_ra8_ok;
287}
288
289/* =============================================================================
290 * Block Write / Block Read (SMBus 3.2 sec 6.5.7 / 6.5.8)
291 * =============================================================================
292 */
293
294ra8_err_t ra8_smbus_block_write(uint8_t target_7b, uint8_t cmd, const uint8_t* data, uint8_t len)
295{
296 if (!s_state.initialized) {
298 }
299 if (len == 0U) {
301 }
302 RA8_CHECK_NULL_PTR(data, s_tag, "block_write: data");
303
304 /* Frame: [cmd] [count] [data...] [optional PEC]. The leading address
305 * byte is generated by the bus write but folded into the PEC by us
306 * here. Max len is 255 by enum constraint, so the scratch is 258. */
307 uint8_t frame[k_smbus_frame_bytes];
308 uint32_t fi = 0U;
309 frame[fi++] = cmd;
310 frame[fi++] = len;
311 for (uint8_t i = 0U; i < len; ++i) {
312 frame[fi++] = data[i];
313 }
314 if (s_state.pec_enabled) {
315 const uint8_t addr_b = internal_make_addr_byte(target_7b, (uint8_t)k_ra8_smbus_rw_write);
316 uint8_t pec = internal_pec_update((uint8_t)k_ra8_smbus_pec_init, addr_b);
317 for (uint32_t i = 0U; i < fi; ++i) {
318 pec = internal_pec_update(pec, frame[i]);
319 }
320 frame[fi++] = pec;
321 }
322 return s_state.bus.write(s_state.bus.ctx, target_7b, frame, fi, true);
323}
324
354 uint8_t cmd,
355 const uint8_t* buf,
356 uint8_t count,
357 uint8_t pec_rx)
358{
359 const uint8_t addr_w = internal_make_addr_byte(target_7b, (uint8_t)k_ra8_smbus_rw_write);
360 const uint8_t addr_r = internal_make_addr_byte(target_7b, (uint8_t)k_ra8_smbus_rw_read);
361 uint8_t pec = internal_pec_update((uint8_t)k_ra8_smbus_pec_init, addr_w);
362 pec = internal_pec_update(pec, cmd);
363 pec = internal_pec_update(pec, addr_r);
364 pec = internal_pec_update(pec, count);
365 for (uint8_t i = 0U; i < count; ++i) {
366 pec = internal_pec_update(pec, buf[i]);
367 }
368 if (pec != pec_rx) {
369 ra8_log_error(s_tag, "block_read: PEC mismatch");
371 }
372 return k_ra8_ok;
373}
374
406static ra8_err_t internal_block_read_finish(uint8_t target_7b,
407 uint8_t cmd,
408 const uint8_t* rx,
409 uint8_t cap,
410 uint8_t* buf,
411 uint8_t* out_len)
412{
413 const uint8_t count = rx[0];
414 *out_len = count;
415 if (count > cap) {
417 }
418 for (uint8_t i = 0U; i < count; ++i) {
419 buf[i] = rx[1U + (uint32_t)i];
420 }
421 if (s_state.pec_enabled) {
422 /* PEC byte sits at rx[1 + count]. */
423 const ra8_err_t pec_err =
424 internal_block_read_pec_check(target_7b, cmd, buf, count, rx[1U + (uint32_t)count]);
425 if (pec_err != k_ra8_ok) {
426 return pec_err;
427 }
428 } /* GCOVR_EXCL_LINE -- reached only when PEC matches; host fake returns constant NTDTBP0 */
429 return k_ra8_ok;
430}
431
433ra8_smbus_block_read(uint8_t target_7b, uint8_t cmd, uint8_t* buf, uint8_t cap, uint8_t* out_len)
434{
435 RA8_CHECK_NULL_PTR(buf, s_tag, "block_read: buf");
436 RA8_CHECK_NULL_PTR(out_len, s_tag, "block_read: out_len");
437 if (!s_state.initialized) {
439 }
440 if (cap == 0U) {
442 }
443
444 /* Read [count] [data...] [optional PEC] in one combined transfer.
445 * We allocate the worst-case 257-byte scratch and read the maximum
446 * the buffer allows to avoid two round trips to peek the count. */
447 uint8_t rx[k_smbus_rx_bytes];
448 const uint32_t want = (uint32_t)cap + 1U + (s_state.pec_enabled ? 1U : 0U);
449 ra8_err_t err = s_state.bus.transfer(s_state.bus.ctx, target_7b, &cmd, 1U, rx, want);
450 if (err != k_ra8_ok) {
451 return err;
452 }
453 return internal_block_read_finish(target_7b, cmd, rx, cap, buf, out_len);
454}
455
456/* =============================================================================
457 * SMBALERT# / Host Notify (SMBus 3.2 sec 6.5.13)
458 * =============================================================================
459 */
460
462{
463 if (!s_state.initialized) {
465 }
466 s_state.alert_fn = fn;
467 s_state.alert_ctx = ctx;
468 return k_ra8_ok;
469}
470
473{
474 if (!s_state.initialized) {
476 }
477 uint8_t ara = 0U;
478 const ra8_err_t err =
479 s_state.bus.read(s_state.bus.ctx, (uint8_t)k_ra8_smbus_alert_addr_7b, &ara, 1U);
480 if (err != k_ra8_ok) {
481 return err;
482 }
483 if (s_state.alert_fn != nullptr) {
484 /* SMBus 3.2 6.5.13: top 7 bits = peripheral address, LSB = status. */
485 const uint8_t addr_7b = (uint8_t)(ara >> (uint32_t)k_ra8_smbus_addr_shift);
486 const uint8_t status = (uint8_t)(ara & 1U);
487 s_state.alert_fn(s_state.alert_ctx, addr_7b, status);
488 }
489 return k_ra8_ok;
490}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_ISR_SAFE
The function is callable from interrupt context.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Error Code Definitions for ra8-firmware.
@ k_ra8_err_crc_mismatch
CRC mismatch detected on received data.
Definition ra8_err.h:423
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Injected I2C-bus seam consumed by Ring-3 I2C device drivers.
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
static uint32_t s_state
ra8_err_t ra8_smbus_write_byte_data(uint8_t target_7b, uint8_t cmd, uint8_t data)
Write Byte Data: register-indexed byte write (SMBus 3.2 sec 6.5.4).
Definition ra8_smbus.c:240
uint8_t ra8_smbus_pec(const uint8_t *data, uint32_t len)
Compute CRC-8/SMBus over a buffer.
Definition ra8_smbus.c:122
ra8_err_t ra8_smbus_init(const ra8_smbus_cfg_t *cfg)
Initialise the SMBus layer over the injected bus seam.
Definition ra8_smbus.c:152
ra8_err_t ra8_smbus_alert_register_callback(ra8_smbus_alert_fn_t fn, void *ctx)
Register a callback fired when ra8_smbus_alert_dispatch successfully reads the Alert Response Address...
Definition ra8_smbus.c:461
ra8_err_t ra8_smbus_send_byte(uint8_t target_7b, uint8_t data)
Send Byte transaction (SMBus 3.2 section 6.5.2).
Definition ra8_smbus.c:191
ra8_err_t ra8_smbus_block_write(uint8_t target_7b, uint8_t cmd, const uint8_t *data, uint8_t len)
Block Write (SMBus 3.2 section 6.5.7).
Definition ra8_smbus.c:294
ra8_err_t ra8_smbus_alert_dispatch(void)
Dispatch a single SMBALERT# event: read the ARA and fire the registered callback.
Definition ra8_smbus.c:472
ra8_err_t ra8_smbus_block_read(uint8_t target_7b, uint8_t cmd, uint8_t *buf, uint8_t cap, uint8_t *out_len)
Block Read (SMBus 3.2 section 6.5.8).
Definition ra8_smbus.c:433
ra8_err_t ra8_smbus_receive_byte(uint8_t target_7b, uint8_t *out_data)
Receive Byte transaction (SMBus 3.2 section 6.5.3).
Definition ra8_smbus.c:210
static uint8_t internal_make_addr_byte(uint8_t target_7b, uint8_t rw_bit)
Definition ra8_smbus.c:141
ra8_err_t ra8_smbus_deinit(void)
Tear the SMBus layer down and release the driver slot.
Definition ra8_smbus.c:175
static uint8_t internal_pec_update(uint8_t crc, uint8_t b)
Update one byte into a running CRC-8/SMBus accumulator.
Definition ra8_smbus.c:109
ra8_smbus_internal_t
Implementation constants.
Definition ra8_smbus.c:46
@ k_ra8_smbus_pec_init
CRC-8 initial value.
Definition ra8_smbus.c:48
@ k_ra8_smbus_byte_mask
8-bit narrowing mask.
Definition ra8_smbus.c:54
@ k_ra8_smbus_addr_shift
7-bit -> 8-bit address shift.
Definition ra8_smbus.c:49
@ k_ra8_smbus_pec_poly
CRC-8 polynomial (SMBus 3.2 5.4).
Definition ra8_smbus.c:47
@ k_ra8_smbus_rw_write
R/W bit value for write.
Definition ra8_smbus.c:50
@ k_ra8_smbus_msb_for_byte
MSB index for byte-loop pec calc.
Definition ra8_smbus.c:52
@ k_ra8_smbus_pec_top_bit
Top bit mask used by pec loop.
Definition ra8_smbus.c:53
@ k_ra8_smbus_rw_read
R/W bit value for read.
Definition ra8_smbus.c:51
smbus_buf_size_t
SMBus scratch buffer sizes (max 255-byte payload + overhead).
Definition ra8_smbus.c:37
@ k_smbus_frame_bytes
Smbus frame bytes.
Definition ra8_smbus.c:38
@ k_smbus_rx_bytes
Smbus RX bytes.
Definition ra8_smbus.c:39
static ra8_err_t internal_block_read_pec_check(uint8_t target_7b, uint8_t cmd, const uint8_t *buf, uint8_t count, uint8_t pec_rx)
Verify the PEC byte of a completed Block Read transfer.
Definition ra8_smbus.c:353
static ra8_err_t internal_block_read_finish(uint8_t target_7b, uint8_t cmd, const uint8_t *rx, uint8_t cap, uint8_t *buf, uint8_t *out_len)
Unpack a completed Block Read transfer into the caller buffer.
Definition ra8_smbus.c:406
ra8_err_t ra8_smbus_read_byte_data(uint8_t target_7b, uint8_t cmd, uint8_t *out_data)
Read Byte Data: register-indexed byte read (SMBus 3.2 sec 6.5.5).
Definition ra8_smbus.c:259
SMBus 3.2 protocol layer over an injected I2C bus seam.
@ k_ra8_smbus_alert_addr_7b
Alert Response Address (ARA).
Definition ra8_smbus.h:75
void(* ra8_smbus_alert_fn_t)(void *ctx, uint8_t target_7b, uint8_t status)
SMBALERT# notification callback.
Definition ra8_smbus.h:101
Caller-filled I2C controller-transfer seam (write / read / write-then-read).
ra8_err_t(* read)(void *ctx, uint8_t addr, uint8_t *data, uint32_t len)
Controller read of len bytes from 7-bit address addr.
ra8_err_t(* transfer)(void *ctx, uint8_t addr, const uint8_t *wr, uint32_t wr_len, uint8_t *rd, uint32_t rd_len)
Combined write-then-RESTART-then-read in one transaction.
ra8_err_t(* write)(void *ctx, uint8_t addr, const uint8_t *data, uint32_t len, bool send_stop)
Controller write of len bytes to 7-bit address addr.
Configuration descriptor for ra8_smbus_init.
Definition ra8_smbus.h:87
ra8_i2c_bus_ops_t bus
Injected I2C transfer seam (app-bound).
Definition ra8_smbus.h:88
bool pec_enabled
Append + verify PEC on every xfer.
Definition ra8_smbus.h:89
Module state: latched at init time.
Definition ra8_smbus.c:64
ra8_i2c_bus_ops_t bus
Injected I2C transfer seam.
Definition ra8_smbus.c:66
bool pec_enabled
Append + verify PEC byte.
Definition ra8_smbus.c:67
void * alert_ctx
Context for alert_fn.
Definition ra8_smbus.c:69
ra8_smbus_alert_fn_t alert_fn
SMBALERT# callback (or NULL).
Definition ra8_smbus.c:68
bool initialized
Latched after a good init.
Definition ra8_smbus.c:65