1 /*
2 * Copyright (c) 2025 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 */
7
8 #include "zephyr/pmci/mctp/mctp_i2c_gpio_common.h"
9 #include <zephyr/sys/__assert.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/uart.h>
12 #include <zephyr/pmci/mctp/mctp_i2c_gpio_target.h>
13 #include <crc-16-ccitt.h>
14
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(mctp_i2c_gpio_target, CONFIG_MCTP_LOG_LEVEL);
17
mctp_i2c_gpio_target_write_requested(struct i2c_target_config * config)18 int mctp_i2c_gpio_target_write_requested(struct i2c_target_config *config)
19 {
20
21 struct mctp_binding_i2c_gpio_target *b =
22 CONTAINER_OF(config, struct mctp_binding_i2c_gpio_target, i2c_target_cfg);
23
24 if (b->rxtx || b->reg_addr == MCTP_I2C_GPIO_INVALID_ADDR) {
25 /* Reset our state */
26 b->reg_addr = MCTP_I2C_GPIO_INVALID_ADDR;
27 b->rxtx = false;
28 b->rx_idx = 0;
29 }
30
31 return 0;
32 }
33
mctp_i2c_gpio_target_write_received(struct i2c_target_config * config,uint8_t val)34 int mctp_i2c_gpio_target_write_received(struct i2c_target_config *config, uint8_t val)
35 {
36 struct mctp_binding_i2c_gpio_target *b =
37 CONTAINER_OF(config, struct mctp_binding_i2c_gpio_target, i2c_target_cfg);
38 int ret = 0;
39
40 switch (b->reg_addr) {
41 case MCTP_I2C_GPIO_INVALID_ADDR:
42 b->rxtx = false;
43 b->reg_addr = val;
44 break;
45 case MCTP_I2C_GPIO_RX_MSG_LEN_ADDR:
46 b->rxtx = true;
47 b->rx_pkt = mctp_pktbuf_alloc(&b->binding, (size_t)val);
48 break;
49 case MCTP_I2C_GPIO_RX_MSG_ADDR:
50 b->rxtx = true;
51 b->rx_pkt->data[b->rx_idx] = val;
52 b->rx_idx += 1;
53
54 /* buffer full */
55 if (b->rx_idx >= b->rx_pkt->size) {
56 ret = -ENOMEM;
57 }
58 break;
59 default:
60 LOG_ERR("Write when reg_addr is %d", b->reg_addr);
61 ret = -EIO;
62 break;
63 }
64
65 return ret;
66 }
67
mctp_i2c_gpio_target_read_requested(struct i2c_target_config * config,uint8_t * val)68 int mctp_i2c_gpio_target_read_requested(struct i2c_target_config *config, uint8_t *val)
69 {
70 struct mctp_binding_i2c_gpio_target *b =
71 CONTAINER_OF(config, struct mctp_binding_i2c_gpio_target, i2c_target_cfg);
72 uint8_t pkt_len;
73 int ret = 0;
74
75 switch (b->reg_addr) {
76 case MCTP_I2C_GPIO_TX_MSG_LEN_ADDR:
77 b->rxtx = true;
78 if (b->tx_pkt == NULL) {
79 LOG_WRN("empty packet?");
80 pkt_len = 0;
81 } else {
82 pkt_len = (uint8_t)(b->tx_pkt->end - b->tx_pkt->start);
83 }
84 *val = pkt_len;
85 break;
86 case MCTP_I2C_GPIO_TX_MSG_ADDR:
87 b->rxtx = true;
88 *val = b->tx_pkt->data[b->tx_pkt->start];
89 b->tx_idx = b->tx_pkt->start;
90 break;
91 default:
92 LOG_WRN("invalid rre reg %d", b->reg_addr);
93 ret = -EINVAL;
94 }
95
96 return ret;
97 }
98
mctp_i2c_gpio_target_read_processed(struct i2c_target_config * config,uint8_t * val)99 int mctp_i2c_gpio_target_read_processed(struct i2c_target_config *config, uint8_t *val)
100 {
101 struct mctp_binding_i2c_gpio_target *b =
102 CONTAINER_OF(config, struct mctp_binding_i2c_gpio_target, i2c_target_cfg);
103
104 b->tx_idx += 1;
105
106 if (b->reg_addr != MCTP_I2C_GPIO_TX_MSG_ADDR) {
107 goto out;
108 }
109
110 if (b->tx_idx > b->tx_pkt->end) {
111 LOG_WRN("rrp past end reg %d", b->reg_addr);
112 return -EIO;
113 }
114
115 *val = b->tx_pkt->data[b->tx_idx];
116
117 out:
118 return 0;
119 }
120
mctp_i2c_gpio_target_stop(struct i2c_target_config * config)121 int mctp_i2c_gpio_target_stop(struct i2c_target_config *config)
122 {
123 struct mctp_binding_i2c_gpio_target *b =
124 CONTAINER_OF(config, struct mctp_binding_i2c_gpio_target, i2c_target_cfg);
125 uint8_t pkt_len;
126
127 if (b->rxtx) {
128 switch (b->reg_addr) {
129 case MCTP_I2C_GPIO_TX_MSG_ADDR:
130 pkt_len = (b->tx_pkt->end - b->tx_pkt->start);
131 if (b->tx_idx < pkt_len - 1) {
132 LOG_WRN("Only %d of %d bytes of the transmit packet were read",
133 b->tx_idx, pkt_len);
134 }
135 b->tx_pkt = NULL;
136 k_sem_give(b->tx_complete);
137 break;
138
139 case MCTP_I2C_GPIO_RX_MSG_ADDR:
140 LOG_DBG("stop rx msg, give pkt");
141 /* Give message to mctp to process */
142 mctp_bus_rx(&b->binding, b->rx_pkt);
143 b->rx_pkt = NULL;
144 break;
145 case MCTP_I2C_GPIO_RX_MSG_LEN_ADDR:
146 case MCTP_I2C_GPIO_TX_MSG_LEN_ADDR:
147 break;
148 default:
149 LOG_WRN("unexpected stop for reg %d", b->reg_addr);
150 break;
151 }
152 }
153
154 return 0;
155 }
156
157 const struct i2c_target_callbacks mctp_i2c_gpio_target_callbacks = {
158 .write_requested = mctp_i2c_gpio_target_write_requested,
159 .read_requested = mctp_i2c_gpio_target_read_requested,
160 .write_received = mctp_i2c_gpio_target_write_received,
161 .read_processed = mctp_i2c_gpio_target_read_processed,
162 .stop = mctp_i2c_gpio_target_stop,
163 };
164
165 /*
166 * libmctp wants us to return once the packet is sent not before
167 * so the entire process of flagging the tx with gpio, waiting on the read,
168 * needs to complete before we can move on.
169 *
170 * this is called for each packet in the packet queue libmctp provides
171 */
mctp_i2c_gpio_target_tx(struct mctp_binding * binding,struct mctp_pktbuf * pkt)172 int mctp_i2c_gpio_target_tx(struct mctp_binding *binding, struct mctp_pktbuf *pkt)
173 {
174 struct mctp_binding_i2c_gpio_target *b =
175 CONTAINER_OF(binding, struct mctp_binding_i2c_gpio_target, binding);
176 int rc;
177
178 k_sem_take(b->tx_lock, K_FOREVER);
179
180 b->tx_pkt = pkt;
181
182 rc = gpio_pin_set_dt(&b->endpoint_gpio, 1);
183 if (rc != 0) {
184 LOG_ERR("failed to set gpio pin");
185 b->tx_pkt = NULL;
186 goto out;
187 }
188
189 k_sem_take(b->tx_complete, K_FOREVER);
190
191 rc = gpio_pin_set_dt(&b->endpoint_gpio, 0);
192 if (rc != 0) {
193 LOG_ERR("failed to clear gpio pin");
194 }
195
196 out:
197 k_sem_give(b->tx_lock);
198 return rc;
199 }
200
mctp_i2c_gpio_target_start(struct mctp_binding * binding)201 int mctp_i2c_gpio_target_start(struct mctp_binding *binding)
202 {
203 struct mctp_binding_i2c_gpio_target *b =
204 CONTAINER_OF(binding, struct mctp_binding_i2c_gpio_target, binding);
205 int rc;
206
207 /* Register i2c target */
208 rc = i2c_target_register(b->i2c, &b->i2c_target_cfg);
209 if (rc != 0) {
210 LOG_ERR("failed to register i2c target");
211 goto out;
212 }
213
214 /* Configure pin to use as data ready signaling */
215 rc = gpio_pin_configure_dt(&b->endpoint_gpio, GPIO_OUTPUT_INACTIVE);
216 if (rc != 0) {
217 LOG_ERR("failed to configure gpio");
218 goto out;
219 }
220
221 mctp_binding_set_tx_enabled(binding, true);
222
223 out:
224 return 0;
225 }
226