Line data Source code
1 0 : /*
2 : * Copyright (c) 2025 Intel Corporation
3 : *
4 : * SPDX-License-Identifier: Apache-2.0
5 : *
6 : */
7 :
8 : #ifndef ZEPHYR_MCTP_I2C_GPIO_CONTROLLER_H_
9 : #define ZEPHYR_MCTP_I2C_GPIO_CONTROLLER_H_
10 :
11 : #include <stdint.h>
12 : #include <zephyr/kernel.h>
13 : #include <zephyr/device.h>
14 : #include <zephyr/drivers/i2c.h>
15 : #include <zephyr/drivers/gpio.h>
16 : #include <zephyr/sys/mpsc_lockfree.h>
17 : #include <zephyr/rtio/rtio.h>
18 : #include <zephyr/pmci/mctp/mctp_i2c_gpio_common.h>
19 :
20 : #include <libmctp.h>
21 : #include <stdatomic.h>
22 :
23 : /** @cond INTERNAL_HIDDEN */
24 :
25 : struct mctp_binding_i2c_gpio_controller;
26 :
27 : struct mctp_i2c_gpio_controller_cb {
28 : struct mpsc_node q;
29 : struct gpio_callback callback;
30 : struct mctp_binding_i2c_gpio_controller *binding;
31 : uint8_t index;
32 : };
33 :
34 : /** INTERNAL_HIDDEN @endcond */
35 :
36 : /**
37 : * @brief An MCTP binding for Zephyr's I2C interface using GPIO
38 : */
39 1 : struct mctp_binding_i2c_gpio_controller {
40 : /** @cond INTERNAL_HIDDEN */
41 : struct mctp_binding binding;
42 : const struct device *i2c;
43 :
44 : uint8_t rx_buf_len;
45 : uint8_t rx_buf[MCTP_I2C_GPIO_MAX_PKT_SIZE];
46 : uint8_t tx_buf[MCTP_I2C_GPIO_MAX_PKT_SIZE];
47 :
48 : struct mctp_pktbuf *rx_pkt;
49 :
50 : /* Avoid needing a thread or work queue task by using RTIO */
51 : struct k_sem *tx_lock;
52 : struct rtio *r_tx;
53 :
54 : /* Mapping of endpoints to i2c targets with gpios */
55 : size_t num_endpoints;
56 : const uint8_t *endpoint_ids;
57 : const struct rtio_iodev **endpoint_iodevs;
58 : const struct gpio_dt_spec *endpoint_gpios;
59 : struct mctp_i2c_gpio_controller_cb *endpoint_gpio_cbs;
60 :
61 : /* Ongoing request from a particular gpio */
62 : struct k_spinlock rx_lock;
63 : struct rtio *r_rx;
64 : struct mpsc rx_q;
65 : struct mctp_i2c_gpio_controller_cb *inflight_rx;
66 :
67 : /** INTERNAL_HIDDEN @endcond */
68 : };
69 :
70 : /** @cond INTERNAL_HIDDEN */
71 : int mctp_i2c_gpio_controller_start(struct mctp_binding *binding);
72 : int mctp_i2c_gpio_controller_tx(struct mctp_binding *binding, struct mctp_pktbuf *pkt);
73 :
74 : #define MCTP_I2C_GPIO_CONTROLLER_IODEV_NAME(_idx, _name) _name##_idx
75 :
76 : #define MCTP_I2C_GPIO_CONTROLLER_IODEV_DEFINE(_node_id, addrs, _idx, _name) \
77 : I2C_IODEV_DEFINE(MCTP_I2C_GPIO_CONTROLLER_IODEV_NAME(_idx, _name), \
78 : DT_PHANDLE(_node_id, i2c), \
79 : DT_PROP_BY_IDX(_node_id, addrs, _idx));
80 :
81 :
82 : #define MCTP_I2C_GPIO_CONTROLLER_DEFINE_GPIOS(_node_id, _name) \
83 : const struct gpio_dt_spec _name##_endpoint_gpios[] = { \
84 : DT_FOREACH_PROP_ELEM_SEP(_node_id, endpoint_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,)) \
85 : }
86 :
87 : #define MCTP_I2C_GPIO_CONTROLLER_DEFINE_IDS(_node_id, _name) \
88 : const uint8_t _name##_endpoint_ids[] = DT_PROP(_node_id, endpoint_ids);
89 :
90 : #define MCTP_I2C_GPIO_CONTROLLER_DEFINE_GPIO_CBS(_node_id, _name) \
91 : struct mctp_i2c_gpio_controller_cb \
92 : _name##_endpoint_gpio_cbs[DT_PROP_LEN(_node_id, endpoint_ids)]
93 :
94 :
95 : #define MCTP_I2C_GPIO_CONTROLLER_DEFINE_IODEVS(_node_id, _name) \
96 : DT_FOREACH_PROP_ELEM_VARGS(_node_id, endpoint_addrs, \
97 : MCTP_I2C_GPIO_CONTROLLER_IODEV_DEFINE, _name) \
98 : const struct rtio_iodev *_name##_endpoint_iodevs[] = { \
99 : LISTIFY(DT_PROP_LEN(_node_id, endpoint_ids), &MCTP_I2C_GPIO_CONTROLLER_IODEV_NAME, \
100 : (,), _name) \
101 : }
102 :
103 : /** INTERNAL_HIDDEN @endcond */
104 :
105 : /**
106 : * @brief Define a MCTP bus binding for I2C controller with GPIO
107 : *
108 : * Rather than mode switching as the MCTP standard wishes, this is a custom
109 : * binding. On the controller side each neighbor has an associated i2c address
110 : * and gpio it will listen for a trigger on. When triggered the controller will
111 : * read the target device. The target device will be expected to have what amounts
112 : * to two registers, one containing a message length and another containing a FIFO
113 : * like register producing the message bytes.
114 : *
115 : * Thus the sequence for a I2C target to send a message would be...
116 : *
117 : * 1. Setup a length and message value (pseudo registers).
118 : * 2. Signal the controller with a GPIO level set
119 : * 3. The controller then is expected to read the length (write of addr 0 + read 1 byte)
120 : * 4. The controller then is expected to read the message (write of addr 1 + read N bytes)
121 : * 5. The target clears the pin set
122 : * 6. The controller reports a message receive to MCTP
123 : *
124 : * @param _name Symbolic name of the bus binding variable
125 : * @param _node_id DeviceTree Node containing the configuration of this MCTP binding
126 : */
127 1 : #define MCTP_I2C_GPIO_CONTROLLER_DT_DEFINE(_name, _node_id) \
128 : RTIO_DEFINE(_name##_rtio_tx, 5, 5); \
129 : RTIO_DEFINE(_name##_rtio_rx, 5, 5); \
130 : MCTP_I2C_GPIO_CONTROLLER_DEFINE_IODEVS(_node_id, _name); \
131 : MCTP_I2C_GPIO_CONTROLLER_DEFINE_GPIOS(_node_id, _name); \
132 : MCTP_I2C_GPIO_CONTROLLER_DEFINE_IDS(_node_id, _name); \
133 : MCTP_I2C_GPIO_CONTROLLER_DEFINE_GPIO_CBS(_node_id, _name); \
134 : K_SEM_DEFINE(_name##_tx_lock, 1, 1); \
135 : struct mctp_binding_i2c_gpio_controller _name = { \
136 : .binding = { \
137 : .name = STRINGIFY(_name), .version = 1, \
138 : .start = mctp_i2c_gpio_controller_start, \
139 : .tx = mctp_i2c_gpio_controller_tx, \
140 : .pkt_size = MCTP_I2C_GPIO_MAX_PKT_SIZE, \
141 : }, \
142 : .i2c = DEVICE_DT_GET(DT_PHANDLE(_node_id, i2c)), \
143 : .num_endpoints = DT_PROP_LEN(_node_id, endpoint_ids), \
144 : .endpoint_ids = _name##_endpoint_ids, \
145 : .endpoint_gpios = _name##_endpoint_gpios, \
146 : .endpoint_gpio_cbs = _name##_endpoint_gpio_cbs, \
147 : .endpoint_iodevs = _name##_endpoint_iodevs, \
148 : .r_tx = &_name##_rtio_tx, \
149 : .r_rx = &_name##_rtio_rx, \
150 : .tx_lock = &_name##_tx_lock, \
151 : };
152 :
153 : #endif /* ZEPHYR_MCTP_I2C_GPIO_CONTROLLER_H_ */
|