xref: /openbmc/libmctp/astlpc.c (revision 4e8264b705d3575bea8312d6467d5fd9fb440caf)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 
3 #if HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #if HAVE_ENDIAN_H
8 #include <endian.h>
9 #endif
10 
11 #include <assert.h>
12 #include <err.h>
13 #include <errno.h>
14 #include <inttypes.h>
15 #include <stdbool.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #define pr_fmt(x) "astlpc: " x
20 
21 #include "libmctp.h"
22 #include "libmctp-alloc.h"
23 #include "libmctp-log.h"
24 #include "libmctp-astlpc.h"
25 #include "container_of.h"
26 
27 #ifdef MCTP_HAVE_FILEIO
28 
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/ioctl.h>
32 #include <sys/mman.h>
33 #include <linux/aspeed-lpc-ctrl.h>
34 
35 /* kernel interface */
36 static const char *kcs_path = "/dev/mctp0";
37 static const char *lpc_path = "/dev/aspeed-lpc-ctrl";
38 
39 #endif
40 
41 struct mctp_astlpc_buffer {
42 	uint32_t offset;
43 	uint32_t size;
44 };
45 
46 struct mctp_astlpc_layout {
47 	struct mctp_astlpc_buffer rx;
48 	struct mctp_astlpc_buffer tx;
49 };
50 
51 struct mctp_binding_astlpc {
52 	struct mctp_binding	binding;
53 
54 	void *lpc_map;
55 	struct mctp_astlpc_layout layout;
56 
57 	uint8_t mode;
58 	uint16_t version;
59 
60 	/* direct ops data */
61 	struct mctp_binding_astlpc_ops ops;
62 	void *ops_data;
63 
64 	/* fileio ops data */
65 	int kcs_fd;
66 	uint8_t kcs_status;
67 
68 	bool			running;
69 };
70 
71 #define binding_to_astlpc(b) \
72 	container_of(b, struct mctp_binding_astlpc, binding)
73 
74 #define astlpc_prlog(ctx, lvl, fmt, ...)                                       \
75 	do {                                                                   \
76 		bool __bmc = ((ctx)->mode == MCTP_BINDING_ASTLPC_MODE_BMC);    \
77 		mctp_prlog(lvl, pr_fmt("%s: " fmt), __bmc ? "bmc" : "host",    \
78 			   ##__VA_ARGS__);                                     \
79 	} while (0)
80 
81 #define astlpc_prerr(ctx, fmt, ...)                                            \
82 	astlpc_prlog(ctx, MCTP_LOG_ERR, fmt, ##__VA_ARGS__)
83 #define astlpc_prwarn(ctx, fmt, ...)                                           \
84 	astlpc_prlog(ctx, MCTP_LOG_WARNING, fmt, ##__VA_ARGS__)
85 #define astlpc_prinfo(ctx, fmt, ...)                                           \
86 	astlpc_prlog(ctx, MCTP_LOG_INFO, fmt, ##__VA_ARGS__)
87 #define astlpc_prdebug(ctx, fmt, ...)                                          \
88 	astlpc_prlog(ctx, MCTP_LOG_DEBUG, fmt, ##__VA_ARGS__)
89 
90 /* clang-format off */
91 #define ASTLPC_MCTP_MAGIC	0x4d435450
92 #define ASTLPC_VER_BAD	0
93 #define ASTLPC_VER_MIN	1
94 #define ASTLPC_VER_CUR	1
95 
96 #define ASTLPC_BODY_SIZE(sz)	((sz) - 4)
97 /* clang-format on */
98 
99 struct mctp_lpcmap_hdr {
100 	uint32_t	magic;
101 
102 	uint16_t	bmc_ver_min;
103 	uint16_t	bmc_ver_cur;
104 	uint16_t	host_ver_min;
105 	uint16_t	host_ver_cur;
106 	uint16_t	negotiated_ver;
107 	uint16_t	pad0;
108 
109 	uint32_t	rx_offset;
110 	uint32_t	rx_size;
111 	uint32_t	tx_offset;
112 	uint32_t	tx_size;
113 } __attribute__((packed));
114 
115 /* layout of TX/RX areas */
116 static const uint32_t	rx_offset = 0x100;
117 static const uint32_t	rx_size   = 0x100;
118 static const uint32_t	tx_offset = 0x200;
119 static const uint32_t	tx_size   = 0x100;
120 
121 #define LPC_WIN_SIZE                (1 * 1024 * 1024)
122 
123 #define KCS_STATUS_BMC_READY		0x80
124 #define KCS_STATUS_CHANNEL_ACTIVE	0x40
125 #define KCS_STATUS_IBF			0x02
126 #define KCS_STATUS_OBF			0x01
127 
128 static inline int mctp_astlpc_kcs_write(struct mctp_binding_astlpc *astlpc,
129 					enum mctp_binding_astlpc_kcs_reg reg,
130 					uint8_t val)
131 {
132 	return astlpc->ops.kcs_write(astlpc->ops_data, reg, val);
133 }
134 
135 static inline int mctp_astlpc_kcs_read(struct mctp_binding_astlpc *astlpc,
136 				       enum mctp_binding_astlpc_kcs_reg reg,
137 				       uint8_t *val)
138 {
139 	return astlpc->ops.kcs_read(astlpc->ops_data, reg, val);
140 }
141 
142 static inline int mctp_astlpc_lpc_write(struct mctp_binding_astlpc *astlpc,
143 					const void *buf, long offset,
144 					size_t len)
145 {
146 	astlpc_prdebug(astlpc, "%s: %zu bytes to 0x%lx", __func__, len, offset);
147 
148 	assert(offset >= 0);
149 
150 	/* Indirect access */
151 	if (astlpc->ops.lpc_write) {
152 		void *data = astlpc->ops_data;
153 
154 		return astlpc->ops.lpc_write(data, buf, offset, len);
155 	}
156 
157 	/* Direct mapping */
158 	assert(astlpc->lpc_map);
159 	memcpy(&((char *)astlpc->lpc_map)[offset], buf, len);
160 
161 	return 0;
162 }
163 
164 static inline int mctp_astlpc_lpc_read(struct mctp_binding_astlpc *astlpc,
165 				       void *buf, long offset, size_t len)
166 {
167 	astlpc_prdebug(astlpc, "%s: %zu bytes from 0x%lx", __func__, len,
168 		       offset);
169 
170 	assert(offset >= 0);
171 
172 	/* Indirect access */
173 	if (astlpc->ops.lpc_read) {
174 		void *data = astlpc->ops_data;
175 
176 		return astlpc->ops.lpc_read(data, buf, offset, len);
177 	}
178 
179 	/* Direct mapping */
180 	assert(astlpc->lpc_map);
181 	memcpy(buf, &((char *)astlpc->lpc_map)[offset], len);
182 
183 	return 0;
184 }
185 
186 static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc,
187 				      uint8_t status)
188 {
189 	uint8_t data;
190 	int rc;
191 
192 	/* Since we're setting the status register, we want the other endpoint
193 	 * to be interrupted. However, some hardware may only raise a host-side
194 	 * interrupt on an ODR event.
195 	 * So, write a dummy value of 0xff to ODR, which will ensure that an
196 	 * interrupt is triggered, and can be ignored by the host.
197 	 */
198 	data = 0xff;
199 	status |= KCS_STATUS_OBF;
200 
201 	rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, status);
202 	if (rc) {
203 		astlpc_prwarn(astlpc, "KCS status write failed");
204 		return -1;
205 	}
206 
207 	rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data);
208 	if (rc) {
209 		astlpc_prwarn(astlpc, "KCS dummy data write failed");
210 		return -1;
211 	}
212 
213 	return 0;
214 }
215 
216 static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc)
217 {
218 	struct mctp_lpcmap_hdr hdr = { 0 };
219 	uint8_t status;
220 
221 	/* Flip the buffers as the names are defined in terms of the host */
222 	astlpc->layout.rx.offset = tx_offset;
223 	astlpc->layout.rx.size = tx_size;
224 	astlpc->layout.tx.offset = rx_offset;
225 	astlpc->layout.tx.size = rx_size;
226 
227 	hdr = (struct mctp_lpcmap_hdr){
228 		.magic = htobe32(ASTLPC_MCTP_MAGIC),
229 		.bmc_ver_min = htobe16(ASTLPC_VER_MIN),
230 		.bmc_ver_cur = htobe16(ASTLPC_VER_CUR),
231 
232 		/* Flip the buffers back as we're now describing the host's
233 		 * configuration to the host */
234 		.rx_offset = htobe32(astlpc->layout.tx.offset),
235 		.rx_size = htobe32(astlpc->layout.tx.size),
236 		.tx_offset = htobe32(astlpc->layout.rx.offset),
237 		.tx_size = htobe32(astlpc->layout.rx.size),
238 	};
239 
240 	mctp_astlpc_lpc_write(astlpc, &hdr, 0, sizeof(hdr));
241 
242 	/* set status indicating that the BMC is now active */
243 	status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
244 	return mctp_astlpc_kcs_set_status(astlpc, status);
245 }
246 
247 static int mctp_binding_astlpc_start_bmc(struct mctp_binding *b)
248 {
249 	struct mctp_binding_astlpc *astlpc =
250 		container_of(b, struct mctp_binding_astlpc, binding);
251 
252 	return mctp_astlpc_init_bmc(astlpc);
253 }
254 
255 static bool mctp_astlpc_validate_version(uint16_t bmc_ver_min,
256 					 uint16_t bmc_ver_cur,
257 					 uint16_t host_ver_min,
258 					 uint16_t host_ver_cur)
259 {
260 	if (!(bmc_ver_min && bmc_ver_cur && host_ver_min && host_ver_cur)) {
261 		mctp_prerr("Invalid version present in [%" PRIu16 ", %" PRIu16
262 			   "], [%" PRIu16 ", %" PRIu16 "]",
263 			   bmc_ver_min, bmc_ver_cur, host_ver_min,
264 			   host_ver_cur);
265 		return false;
266 	} else if (bmc_ver_min > bmc_ver_cur) {
267 		mctp_prerr("Invalid bmc version range [%" PRIu16 ", %" PRIu16
268 			   "]",
269 			   bmc_ver_min, bmc_ver_cur);
270 		return false;
271 	} else if (host_ver_min > host_ver_cur) {
272 		mctp_prerr("Invalid host version range [%" PRIu16 ", %" PRIu16
273 			   "]",
274 			   host_ver_min, host_ver_cur);
275 		return false;
276 	} else if ((host_ver_cur < bmc_ver_min) ||
277 		   (host_ver_min > bmc_ver_cur)) {
278 		mctp_prerr(
279 			"Unable to satisfy version negotiation with ranges [%" PRIu16
280 			", %" PRIu16 "] and [%" PRIu16 ", %" PRIu16 "]",
281 			bmc_ver_min, bmc_ver_cur, host_ver_min, host_ver_cur);
282 		return false;
283 	}
284 
285 	return true;
286 }
287 
288 static int mctp_astlpc_init_host(struct mctp_binding_astlpc *astlpc)
289 {
290 	const uint16_t ver_min_be = htobe16(ASTLPC_VER_MIN);
291 	const uint16_t ver_cur_be = htobe16(ASTLPC_VER_CUR);
292 	uint16_t bmc_ver_min, bmc_ver_cur;
293 	struct mctp_lpcmap_hdr hdr;
294 	uint8_t status;
295 	int rc;
296 
297 	rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
298 	if (rc) {
299 		mctp_prwarn("KCS status read failed");
300 		return rc;
301 	}
302 
303 	astlpc->kcs_status = status;
304 
305 	if (!(status & KCS_STATUS_BMC_READY))
306 		return -EHOSTDOWN;
307 
308 	mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
309 
310 	astlpc->layout.rx.offset = be32toh(hdr.rx_offset);
311 	astlpc->layout.rx.size = be32toh(hdr.rx_size);
312 	astlpc->layout.tx.offset = be32toh(hdr.tx_offset);
313 	astlpc->layout.tx.size = be32toh(hdr.tx_size);
314 
315 	bmc_ver_min = be16toh(hdr.bmc_ver_min);
316 	bmc_ver_cur = be16toh(hdr.bmc_ver_cur);
317 
318 	if (!mctp_astlpc_validate_version(bmc_ver_min, bmc_ver_cur,
319 					  ASTLPC_VER_MIN, ASTLPC_VER_CUR)) {
320 		astlpc_prerr(astlpc, "Cannot negotiate with invalid versions");
321 		return -EINVAL;
322 	}
323 
324 	mctp_astlpc_lpc_write(astlpc, &ver_min_be,
325 			      offsetof(struct mctp_lpcmap_hdr, host_ver_min),
326 			      sizeof(ver_min_be));
327 
328 	mctp_astlpc_lpc_write(astlpc, &ver_cur_be,
329 			      offsetof(struct mctp_lpcmap_hdr, host_ver_cur),
330 			      sizeof(ver_cur_be));
331 
332 	/* Send channel init command */
333 	rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, 0x0);
334 	if (rc) {
335 		astlpc_prwarn(astlpc, "KCS write failed");
336 	}
337 
338 	return rc;
339 }
340 
341 static int mctp_binding_astlpc_start_host(struct mctp_binding *b)
342 {
343 	struct mctp_binding_astlpc *astlpc =
344 		container_of(b, struct mctp_binding_astlpc, binding);
345 
346 	return mctp_astlpc_init_host(astlpc);
347 }
348 
349 static bool __mctp_astlpc_kcs_ready(struct mctp_binding_astlpc *astlpc,
350 				    uint8_t status, bool is_write)
351 {
352 	bool is_bmc;
353 	bool ready_state;
354 	uint8_t flag;
355 
356 	is_bmc = (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC);
357 	flag = (is_bmc ^ is_write) ? KCS_STATUS_IBF : KCS_STATUS_OBF;
358 	ready_state = is_write ? 0 : 1;
359 
360 	return !!(status & flag) == ready_state;
361 }
362 
363 static inline bool
364 mctp_astlpc_kcs_read_ready(struct mctp_binding_astlpc *astlpc, uint8_t status)
365 {
366 	return __mctp_astlpc_kcs_ready(astlpc, status, false);
367 }
368 
369 static inline bool
370 mctp_astlpc_kcs_write_ready(struct mctp_binding_astlpc *astlpc, uint8_t status)
371 {
372 	return __mctp_astlpc_kcs_ready(astlpc, status, true);
373 }
374 
375 static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc,
376 		uint8_t data)
377 {
378 	uint8_t status;
379 	int rc;
380 
381 	for (;;) {
382 		rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS,
383 					  &status);
384 		if (rc) {
385 			astlpc_prwarn(astlpc, "KCS status read failed");
386 			return -1;
387 		}
388 		if (mctp_astlpc_kcs_write_ready(astlpc, status))
389 			break;
390 		/* todo: timeout */
391 	}
392 
393 	rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data);
394 	if (rc) {
395 		astlpc_prwarn(astlpc, "KCS data write failed");
396 		return -1;
397 	}
398 
399 	return 0;
400 }
401 
402 static int mctp_binding_astlpc_tx(struct mctp_binding *b,
403 		struct mctp_pktbuf *pkt)
404 {
405 	struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b);
406 	uint32_t len, len_be;
407 	struct mctp_hdr *hdr;
408 
409 	hdr = mctp_pktbuf_hdr(pkt);
410 	len = mctp_pktbuf_size(pkt);
411 
412 	astlpc_prdebug(astlpc,
413 		       "%s: Transmitting %" PRIu32
414 		       "-byte packet (%hhu, %hhu, 0x%hhx)",
415 		       __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag);
416 
417 	if (len > ASTLPC_BODY_SIZE(astlpc->layout.tx.size)) {
418 		astlpc_prwarn(astlpc, "invalid TX len 0x%x", len);
419 		return -1;
420 	}
421 
422 	len_be = htobe32(len);
423 	mctp_astlpc_lpc_write(astlpc, &len_be, astlpc->layout.tx.offset,
424 			      sizeof(len_be));
425 	mctp_astlpc_lpc_write(astlpc, hdr, astlpc->layout.tx.offset + 4, len);
426 
427 	mctp_binding_set_tx_enabled(b, false);
428 
429 	mctp_astlpc_kcs_send(astlpc, 0x1);
430 	return 0;
431 }
432 
433 static uint16_t mctp_astlpc_negotiate_version(uint16_t bmc_ver_min,
434 					      uint16_t bmc_ver_cur,
435 					      uint16_t host_ver_min,
436 					      uint16_t host_ver_cur)
437 {
438 	if (!mctp_astlpc_validate_version(bmc_ver_min, bmc_ver_cur,
439 					  host_ver_min, host_ver_cur))
440 		return ASTLPC_VER_BAD;
441 
442 	if (bmc_ver_cur < host_ver_cur)
443 		return bmc_ver_cur;
444 
445 	return host_ver_cur;
446 }
447 
448 static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc)
449 {
450 	uint16_t negotiated, negotiated_be;
451 	struct mctp_lpcmap_hdr hdr;
452 	uint8_t status;
453 
454 	mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
455 
456 	/* Version negotiation */
457 	negotiated =
458 		mctp_astlpc_negotiate_version(ASTLPC_VER_MIN, ASTLPC_VER_CUR,
459 					      be16toh(hdr.host_ver_min),
460 					      be16toh(hdr.host_ver_cur));
461 
462 	/* Populate the negotiated version */
463 	astlpc->version = negotiated;
464 	negotiated_be = htobe16(negotiated);
465 	mctp_astlpc_lpc_write(astlpc, &negotiated_be,
466 			      offsetof(struct mctp_lpcmap_hdr, negotiated_ver),
467 			      sizeof(negotiated_be));
468 
469 	/* Finalise the configuration */
470 	status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
471 	if (negotiated > 0) {
472 		astlpc_prinfo(astlpc, "Negotiated binding version %" PRIu16,
473 			      negotiated);
474 		status |= KCS_STATUS_CHANNEL_ACTIVE;
475 	} else {
476 		astlpc_prerr(astlpc, "Failed to initialise channel\n");
477 	}
478 
479 	mctp_astlpc_kcs_set_status(astlpc, status);
480 
481 	mctp_binding_set_tx_enabled(&astlpc->binding,
482 				    status & KCS_STATUS_CHANNEL_ACTIVE);
483 }
484 
485 static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
486 {
487 	struct mctp_pktbuf *pkt;
488 	uint32_t len;
489 
490 	mctp_astlpc_lpc_read(astlpc, &len, astlpc->layout.rx.offset,
491 			     sizeof(len));
492 	len = be32toh(len);
493 
494 	if (len > ASTLPC_BODY_SIZE(astlpc->layout.rx.size)) {
495 		astlpc_prwarn(astlpc, "invalid RX len 0x%x", len);
496 		return;
497 	}
498 
499 	assert(astlpc->binding.pkt_size >= 0);
500 	if (len > (uint32_t)astlpc->binding.pkt_size) {
501 		mctp_prwarn("invalid RX len 0x%x", len);
502 		astlpc_prwarn(astlpc, "invalid RX len 0x%x", len);
503 		return;
504 	}
505 
506 	pkt = mctp_pktbuf_alloc(&astlpc->binding, len);
507 	if (!pkt)
508 		goto out_complete;
509 
510 	mctp_astlpc_lpc_read(astlpc, mctp_pktbuf_hdr(pkt),
511 			     astlpc->layout.rx.offset + 4, len);
512 
513 	mctp_bus_rx(&astlpc->binding, pkt);
514 
515 out_complete:
516 	mctp_astlpc_kcs_send(astlpc, 0x2);
517 }
518 
519 static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc)
520 {
521 	mctp_binding_set_tx_enabled(&astlpc->binding, true);
522 }
523 
524 static int mctp_astlpc_finalise_channel(struct mctp_binding_astlpc *astlpc)
525 {
526 	uint16_t negotiated;
527 	int rc;
528 
529 	rc = mctp_astlpc_lpc_read(astlpc, &negotiated,
530 				  offsetof(struct mctp_lpcmap_hdr,
531 					   negotiated_ver),
532 				  sizeof(negotiated));
533 	if (rc < 0)
534 		return rc;
535 
536 	negotiated = be16toh(negotiated);
537 
538 	if (negotiated == ASTLPC_VER_BAD || negotiated < ASTLPC_VER_MIN ||
539 	    negotiated > ASTLPC_VER_CUR) {
540 		astlpc_prerr(astlpc, "Failed to negotiate version, got: %u\n",
541 			     negotiated);
542 		return -EINVAL;
543 	}
544 
545 	astlpc->version = negotiated;
546 
547 	return 0;
548 }
549 
550 static int mctp_astlpc_update_channel(struct mctp_binding_astlpc *astlpc,
551 				      uint8_t status)
552 {
553 	uint8_t updated;
554 	int rc = 0;
555 
556 	assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
557 
558 	updated = astlpc->kcs_status ^ status;
559 
560 	astlpc_prdebug(astlpc, "%s: status: 0x%x, update: 0x%x", __func__,
561 		       status, updated);
562 
563 	if (updated & KCS_STATUS_BMC_READY) {
564 		if (status & KCS_STATUS_BMC_READY) {
565 			astlpc->kcs_status = status;
566 			return astlpc->binding.start(&astlpc->binding);
567 		} else {
568 			mctp_binding_set_tx_enabled(&astlpc->binding, false);
569 		}
570 	}
571 
572 	if (astlpc->version == 0 || updated & KCS_STATUS_CHANNEL_ACTIVE) {
573 		bool enable;
574 
575 		rc = mctp_astlpc_finalise_channel(astlpc);
576 		enable = (status & KCS_STATUS_CHANNEL_ACTIVE) && rc == 0;
577 
578 		mctp_binding_set_tx_enabled(&astlpc->binding, enable);
579 	}
580 
581 	astlpc->kcs_status = status;
582 
583 	return rc;
584 }
585 
586 int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
587 {
588 	uint8_t status, data;
589 	int rc;
590 
591 	rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
592 	if (rc) {
593 		astlpc_prwarn(astlpc, "KCS read error");
594 		return -1;
595 	}
596 
597 	astlpc_prdebug(astlpc, "%s: status: 0x%hhx", __func__, status);
598 
599 	if (!mctp_astlpc_kcs_read_ready(astlpc, status))
600 		return 0;
601 
602 	rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_DATA, &data);
603 	if (rc) {
604 		astlpc_prwarn(astlpc, "KCS data read error");
605 		return -1;
606 	}
607 
608 	astlpc_prdebug(astlpc, "%s: data: 0x%hhx", __func__, data);
609 
610 	switch (data) {
611 	case 0x0:
612 		mctp_astlpc_init_channel(astlpc);
613 		break;
614 	case 0x1:
615 		mctp_astlpc_rx_start(astlpc);
616 		break;
617 	case 0x2:
618 		mctp_astlpc_tx_complete(astlpc);
619 		break;
620 	case 0xff:
621 		/* No responsibilities for the BMC on 0xff */
622 		if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) {
623 			rc = mctp_astlpc_update_channel(astlpc, status);
624 			if (rc < 0)
625 				return rc;
626 		}
627 		break;
628 	default:
629 		astlpc_prwarn(astlpc, "unknown message 0x%x", data);
630 	}
631 
632 	/* Handle silent loss of bmc-ready */
633 	if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) {
634 		if (!(status & KCS_STATUS_BMC_READY && data == 0xff))
635 			return mctp_astlpc_update_channel(astlpc, status);
636 	}
637 
638 	return rc;
639 }
640 
641 /* allocate and basic initialisation */
642 static struct mctp_binding_astlpc *__mctp_astlpc_init(uint8_t mode,
643 						      uint32_t mtu)
644 {
645 	struct mctp_binding_astlpc *astlpc;
646 
647 	assert((mode == MCTP_BINDING_ASTLPC_MODE_BMC) ||
648 	       (mode == MCTP_BINDING_ASTLPC_MODE_HOST));
649 
650 	astlpc = __mctp_alloc(sizeof(*astlpc));
651 	if (!astlpc)
652 		return NULL;
653 
654 	memset(astlpc, 0, sizeof(*astlpc));
655 	astlpc->mode = mode;
656 	astlpc->lpc_map = NULL;
657 	astlpc->binding.name = "astlpc";
658 	astlpc->binding.version = 1;
659 	astlpc->binding.pkt_size = MCTP_PACKET_SIZE(mtu);
660 	astlpc->binding.pkt_pad = 0;
661 	astlpc->binding.tx = mctp_binding_astlpc_tx;
662 	if (mode == MCTP_BINDING_ASTLPC_MODE_BMC)
663 		astlpc->binding.start = mctp_binding_astlpc_start_bmc;
664 	else if (mode == MCTP_BINDING_ASTLPC_MODE_HOST)
665 		astlpc->binding.start = mctp_binding_astlpc_start_host;
666 	else {
667 		astlpc_prerr(astlpc, "%s: Invalid mode: %d\n", __func__, mode);
668 		__mctp_free(astlpc);
669 		return NULL;
670 	}
671 
672 	return astlpc;
673 }
674 
675 struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b)
676 {
677 	return &b->binding;
678 }
679 
680 struct mctp_binding_astlpc *
681 mctp_astlpc_init(uint8_t mode, uint32_t mtu, void *lpc_map,
682 		 const struct mctp_binding_astlpc_ops *ops, void *ops_data)
683 {
684 	struct mctp_binding_astlpc *astlpc;
685 
686 	if (!(mode == MCTP_BINDING_ASTLPC_MODE_BMC ||
687 	      mode == MCTP_BINDING_ASTLPC_MODE_HOST)) {
688 		mctp_prerr("Unknown binding mode: %u", mode);
689 		return NULL;
690 	}
691 
692 	if (mtu != MCTP_BTU) {
693 		mctp_prwarn("Unable to negotiate the MTU, using %u instead",
694 			    MCTP_BTU);
695 		mtu = MCTP_BTU;
696 	}
697 
698 	astlpc = __mctp_astlpc_init(mode, mtu);
699 	if (!astlpc)
700 		return NULL;
701 
702 	memcpy(&astlpc->ops, ops, sizeof(astlpc->ops));
703 	astlpc->ops_data = ops_data;
704 	astlpc->lpc_map = lpc_map;
705 	astlpc->mode = mode;
706 
707 	return astlpc;
708 }
709 
710 struct mctp_binding_astlpc *
711 mctp_astlpc_init_ops(const struct mctp_binding_astlpc_ops *ops, void *ops_data,
712 		     void *lpc_map)
713 {
714 	return mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, MCTP_BTU, lpc_map,
715 				ops, ops_data);
716 }
717 
718 void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc)
719 {
720 	/* Clear channel-active and bmc-ready */
721 	if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC)
722 		mctp_astlpc_kcs_set_status(astlpc, KCS_STATUS_OBF);
723 	__mctp_free(astlpc);
724 }
725 
726 #ifdef MCTP_HAVE_FILEIO
727 
728 static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc)
729 {
730 	struct aspeed_lpc_ctrl_mapping map = {
731 		.window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
732 		.window_id = 0, /* There's only one */
733 		.flags = 0,
734 		.addr = 0,
735 		.offset = 0,
736 		.size = 0
737 	};
738 	void *lpc_map_base;
739 	int fd, rc;
740 
741 	fd = open(lpc_path, O_RDWR | O_SYNC);
742 	if (fd < 0) {
743 		astlpc_prwarn(astlpc, "LPC open (%s) failed", lpc_path);
744 		return -1;
745 	}
746 
747 	rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map);
748 	if (rc) {
749 		astlpc_prwarn(astlpc, "LPC GET_SIZE failed");
750 		close(fd);
751 		return -1;
752 	}
753 
754 	lpc_map_base =
755 		mmap(NULL, map.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
756 	if (lpc_map_base == MAP_FAILED) {
757 		astlpc_prwarn(astlpc, "LPC mmap failed");
758 		rc = -1;
759 	} else {
760 		astlpc->lpc_map = lpc_map_base + map.size - LPC_WIN_SIZE;
761 	}
762 
763 	close(fd);
764 
765 	return rc;
766 }
767 
768 static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc)
769 {
770 	astlpc->kcs_fd = open(kcs_path, O_RDWR);
771 	if (astlpc->kcs_fd < 0)
772 		return -1;
773 
774 	return 0;
775 }
776 
777 static int __mctp_astlpc_fileio_kcs_read(void *arg,
778 		enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val)
779 {
780 	struct mctp_binding_astlpc *astlpc = arg;
781 	off_t offset = reg;
782 	int rc;
783 
784 	rc = pread(astlpc->kcs_fd, val, 1, offset);
785 
786 	return rc == 1 ? 0 : -1;
787 }
788 
789 static int __mctp_astlpc_fileio_kcs_write(void *arg,
790 		enum mctp_binding_astlpc_kcs_reg reg, uint8_t val)
791 {
792 	struct mctp_binding_astlpc *astlpc = arg;
793 	off_t offset = reg;
794 	int rc;
795 
796 	rc = pwrite(astlpc->kcs_fd, &val, 1, offset);
797 
798 	return rc == 1 ? 0 : -1;
799 }
800 
801 int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc)
802 {
803 	return astlpc->kcs_fd;
804 }
805 
806 struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void)
807 {
808 	struct mctp_binding_astlpc *astlpc;
809 	int rc;
810 
811 	/*
812 	 * If we're doing file IO then we're very likely not running
813 	 * freestanding, so lets assume that we're on the BMC side
814 	 */
815 	astlpc = __mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, MCTP_BTU);
816 	if (!astlpc)
817 		return NULL;
818 
819 	/* Set internal operations for kcs. We use direct accesses to the lpc
820 	 * map area */
821 	astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read;
822 	astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write;
823 	astlpc->ops_data = astlpc;
824 
825 	rc = mctp_astlpc_init_fileio_lpc(astlpc);
826 	if (rc) {
827 		free(astlpc);
828 		return NULL;
829 	}
830 
831 	rc = mctp_astlpc_init_fileio_kcs(astlpc);
832 	if (rc) {
833 		free(astlpc);
834 		return NULL;
835 	}
836 
837 	return astlpc;
838 }
839 #else
840 struct mctp_binding_astlpc * __attribute__((const))
841 	mctp_astlpc_init_fileio(void)
842 {
843 	astlpc_prerr(astlpc, "Missing support for file IO");
844 	return NULL;
845 }
846 
847 int __attribute__((const)) mctp_astlpc_get_fd(
848 		struct mctp_binding_astlpc *astlpc __attribute__((unused)))
849 {
850 	astlpc_prerr(astlpc, "Missing support for file IO");
851 	return -1;
852 }
853 #endif
854