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