blob: 0cfd8562d5c8d56f729c1ca430abfb6a57d61293 [file] [log] [blame]
Jeremy Kerr3d36ee22019-05-30 11:15:37 +08001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
Jeremy Kerr672c8852019-03-01 12:18:07 +08002
Andrew Jeffery3286f172020-03-17 23:04:13 +10303#if HAVE_CONFIG_H
4#include "config.h"
5#endif
6
7#if HAVE_ENDIAN_H
Jeremy Kerr92a10a62019-08-28 16:55:54 +05308#include <endian.h>
Andrew Jeffery3286f172020-03-17 23:04:13 +10309#endif
10
11#include <assert.h>
Andrew Jeffery59c6a5c2020-01-17 15:52:51 +103012#include <err.h>
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093013#include <errno.h>
Andrew Jefferyedfe3832020-02-06 11:52:11 +103014#include <inttypes.h>
Jeremy Kerr672c8852019-03-01 12:18:07 +080015#include <stdbool.h>
16#include <stdlib.h>
17#include <string.h>
Jeremy Kerr672c8852019-03-01 12:18:07 +080018
Jeremy Kerr672c8852019-03-01 12:18:07 +080019#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"
Przemyslaw Czarnowskiff25d7e2020-03-26 11:39:37 +010025#include "container_of.h"
Jeremy Kerr672c8852019-03-01 12:18:07 +080026
Jeremy Kerrb214c642019-11-27 11:34:00 +080027#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr92a10a62019-08-28 16:55:54 +053028
Jeremy Kerrc6f676d2019-12-19 09:24:06 +080029#include <unistd.h>
Jeremy Kerr92a10a62019-08-28 16:55:54 +053030#include <fcntl.h>
31#include <sys/ioctl.h>
32#include <sys/mman.h>
33#include <linux/aspeed-lpc-ctrl.h>
34
35/* kernel interface */
36static const char *kcs_path = "/dev/mctp0";
37static const char *lpc_path = "/dev/aspeed-lpc-ctrl";
38
39#endif
40
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093041struct mctp_astlpc_buffer {
42 uint32_t offset;
43 uint32_t size;
44};
45
46struct mctp_astlpc_layout {
47 struct mctp_astlpc_buffer rx;
48 struct mctp_astlpc_buffer tx;
49};
50
Jeremy Kerr672c8852019-03-01 12:18:07 +080051struct mctp_binding_astlpc {
52 struct mctp_binding binding;
Jeremy Kerrbc53d352019-08-28 14:26:14 +053053
Andrew Jeffery55fb90b2020-05-12 13:54:37 +093054 void *lpc_map;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093055 struct mctp_astlpc_layout layout;
56
57 uint8_t mode;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +093058 uint16_t version;
Andrew Jefferya9368982020-06-09 13:07:39 +093059 uint32_t requested_mtu;
Jeremy Kerrbc53d352019-08-28 14:26:14 +053060
61 /* direct ops data */
Andrew Jeffery55fb90b2020-05-12 13:54:37 +093062 struct mctp_binding_astlpc_ops ops;
63 void *ops_data;
Jeremy Kerrbc53d352019-08-28 14:26:14 +053064
65 /* fileio ops data */
Andrew Jeffery979c6a12020-05-23 20:04:49 +093066 int kcs_fd;
67 uint8_t kcs_status;
Jeremy Kerr672c8852019-03-01 12:18:07 +080068
69 bool running;
Jeremy Kerr672c8852019-03-01 12:18:07 +080070};
71
Jeremy Kerr672c8852019-03-01 12:18:07 +080072#define binding_to_astlpc(b) \
73 container_of(b, struct mctp_binding_astlpc, binding)
74
Andrew Jeffery9101a2a2020-05-22 16:08:03 +093075#define astlpc_prlog(ctx, lvl, fmt, ...) \
76 do { \
77 bool __bmc = ((ctx)->mode == MCTP_BINDING_ASTLPC_MODE_BMC); \
78 mctp_prlog(lvl, pr_fmt("%s: " fmt), __bmc ? "bmc" : "host", \
79 ##__VA_ARGS__); \
80 } while (0)
81
82#define astlpc_prerr(ctx, fmt, ...) \
83 astlpc_prlog(ctx, MCTP_LOG_ERR, fmt, ##__VA_ARGS__)
84#define astlpc_prwarn(ctx, fmt, ...) \
85 astlpc_prlog(ctx, MCTP_LOG_WARNING, fmt, ##__VA_ARGS__)
86#define astlpc_prinfo(ctx, fmt, ...) \
87 astlpc_prlog(ctx, MCTP_LOG_INFO, fmt, ##__VA_ARGS__)
88#define astlpc_prdebug(ctx, fmt, ...) \
89 astlpc_prlog(ctx, MCTP_LOG_DEBUG, fmt, ##__VA_ARGS__)
90
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093091/* clang-format off */
92#define ASTLPC_MCTP_MAGIC 0x4d435450
Andrew Jeffery4e8264b2020-05-23 20:34:33 +093093#define ASTLPC_VER_BAD 0
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093094#define ASTLPC_VER_MIN 1
Andrew Jeffery7cd72f12020-05-12 20:27:59 +093095
Andrew Jeffery3a540662020-05-26 19:55:30 +093096/* Support testing of new binding protocols */
97#ifndef ASTLPC_VER_CUR
Andrew Jeffery5303d9c2020-06-09 13:37:46 +093098#define ASTLPC_VER_CUR 2
Andrew Jeffery3a540662020-05-26 19:55:30 +093099#endif
100
101#define ASTLPC_PACKET_SIZE(sz) (4 + (sz))
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930102#define ASTLPC_BODY_SIZE(sz) ((sz) - 4)
103/* clang-format on */
Jeremy Kerr672c8852019-03-01 12:18:07 +0800104
105struct mctp_lpcmap_hdr {
Andrew Jeffery3a540662020-05-26 19:55:30 +0930106 uint32_t magic;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800107
Andrew Jeffery3a540662020-05-26 19:55:30 +0930108 uint16_t bmc_ver_min;
109 uint16_t bmc_ver_cur;
110 uint16_t host_ver_min;
111 uint16_t host_ver_cur;
112 uint16_t negotiated_ver;
113 uint16_t pad0;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800114
Andrew Jeffery3a540662020-05-26 19:55:30 +0930115 struct {
116 uint32_t rx_offset;
117 uint32_t rx_size;
118 uint32_t tx_offset;
119 uint32_t tx_size;
120 } layout;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800121} __attribute__((packed));
122
Andrew Jeffery3a540662020-05-26 19:55:30 +0930123static const uint32_t control_size = 0x100;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800124
Jeremy Kerr672c8852019-03-01 12:18:07 +0800125#define LPC_WIN_SIZE (1 * 1024 * 1024)
126
Jeremy Kerr672c8852019-03-01 12:18:07 +0800127#define KCS_STATUS_BMC_READY 0x80
128#define KCS_STATUS_CHANNEL_ACTIVE 0x40
129#define KCS_STATUS_IBF 0x02
130#define KCS_STATUS_OBF 0x01
131
Andrew Jeffery3a540662020-05-26 19:55:30 +0930132#define MIN(a, b) \
133 ({ \
134 typeof(a) _a = a; \
135 typeof(b) _b = b; \
136 _a < _b ? _a : _b; \
137 })
138
139#define MAX(a, b) \
140 ({ \
141 typeof(a) _a = a; \
142 typeof(b) _b = b; \
143 _a > _b ? _a : _b; \
144 })
145
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930146static inline int mctp_astlpc_kcs_write(struct mctp_binding_astlpc *astlpc,
147 enum mctp_binding_astlpc_kcs_reg reg,
148 uint8_t val)
149{
150 return astlpc->ops.kcs_write(astlpc->ops_data, reg, val);
151}
152
153static inline int mctp_astlpc_kcs_read(struct mctp_binding_astlpc *astlpc,
154 enum mctp_binding_astlpc_kcs_reg reg,
155 uint8_t *val)
156{
157 return astlpc->ops.kcs_read(astlpc->ops_data, reg, val);
158}
159
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930160static inline int mctp_astlpc_lpc_write(struct mctp_binding_astlpc *astlpc,
161 const void *buf, long offset,
162 size_t len)
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530163{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930164 astlpc_prdebug(astlpc, "%s: %zu bytes to 0x%lx", __func__, len, offset);
165
166 assert(offset >= 0);
167
168 /* Indirect access */
169 if (astlpc->ops.lpc_write) {
170 void *data = astlpc->ops_data;
171
172 return astlpc->ops.lpc_write(data, buf, offset, len);
173 }
174
175 /* Direct mapping */
176 assert(astlpc->lpc_map);
177 memcpy(&((char *)astlpc->lpc_map)[offset], buf, len);
178
179 return 0;
180}
181
182static inline int mctp_astlpc_lpc_read(struct mctp_binding_astlpc *astlpc,
183 void *buf, long offset, size_t len)
184{
185 astlpc_prdebug(astlpc, "%s: %zu bytes from 0x%lx", __func__, len,
186 offset);
187
188 assert(offset >= 0);
189
190 /* Indirect access */
191 if (astlpc->ops.lpc_read) {
192 void *data = astlpc->ops_data;
193
194 return astlpc->ops.lpc_read(data, buf, offset, len);
195 }
196
197 /* Direct mapping */
198 assert(astlpc->lpc_map);
199 memcpy(buf, &((char *)astlpc->lpc_map)[offset], len);
200
201 return 0;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530202}
203
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930204static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc,
205 uint8_t status)
206{
207 uint8_t data;
208 int rc;
209
210 /* Since we're setting the status register, we want the other endpoint
211 * to be interrupted. However, some hardware may only raise a host-side
212 * interrupt on an ODR event.
213 * So, write a dummy value of 0xff to ODR, which will ensure that an
214 * interrupt is triggered, and can be ignored by the host.
215 */
216 data = 0xff;
217 status |= KCS_STATUS_OBF;
218
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930219 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, status);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930220 if (rc) {
221 astlpc_prwarn(astlpc, "KCS status write failed");
222 return -1;
223 }
224
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930225 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930226 if (rc) {
227 astlpc_prwarn(astlpc, "KCS dummy data write failed");
228 return -1;
229 }
230
231 return 0;
232}
233
Andrew Jeffery3a540662020-05-26 19:55:30 +0930234static int mctp_astlpc_layout_read(struct mctp_binding_astlpc *astlpc,
235 struct mctp_astlpc_layout *layout)
236{
237 struct mctp_lpcmap_hdr hdr;
238 int rc;
239
240 rc = mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
241 if (rc < 0)
242 return rc;
243
244 /* Flip the buffers as the names are defined in terms of the host */
245 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) {
246 layout->rx.offset = be32toh(hdr.layout.tx_offset);
247 layout->rx.size = be32toh(hdr.layout.tx_size);
248 layout->tx.offset = be32toh(hdr.layout.rx_offset);
249 layout->tx.size = be32toh(hdr.layout.rx_size);
250 } else {
251 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
252
253 layout->rx.offset = be32toh(hdr.layout.rx_offset);
254 layout->rx.size = be32toh(hdr.layout.rx_size);
255 layout->tx.offset = be32toh(hdr.layout.tx_offset);
256 layout->tx.size = be32toh(hdr.layout.tx_size);
257 }
258
259 return 0;
260}
261
262static int mctp_astlpc_layout_write(struct mctp_binding_astlpc *astlpc,
263 struct mctp_astlpc_layout *layout)
264{
265 uint32_t rx_size_be;
266
267 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) {
268 struct mctp_lpcmap_hdr hdr;
269
270 /*
271 * Flip the buffers as the names are defined in terms of the
272 * host
273 */
274 hdr.layout.rx_offset = htobe32(layout->tx.offset);
275 hdr.layout.rx_size = htobe32(layout->tx.size);
276 hdr.layout.tx_offset = htobe32(layout->rx.offset);
277 hdr.layout.tx_size = htobe32(layout->rx.size);
278
279 return mctp_astlpc_lpc_write(astlpc, &hdr.layout,
280 offsetof(struct mctp_lpcmap_hdr, layout),
281 sizeof(hdr.layout));
282 }
283
284 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
285
286 /*
287 * As of v2 we only need to write rx_size - the offsets are controlled
288 * by the BMC, as is the BMC's rx_size (host tx_size).
289 */
290 rx_size_be = htobe32(layout->rx.size);
291 return mctp_astlpc_lpc_write(astlpc, &rx_size_be,
292 offsetof(struct mctp_lpcmap_hdr, layout.rx_size),
293 sizeof(rx_size_be));
294}
295
296static bool mctp_astlpc_buffer_validate(struct mctp_astlpc_buffer *buf,
297 const char *name)
298{
299 /* Check for overflow */
300 if (buf->offset + buf->size < buf->offset) {
301 mctp_prerr(
302 "%s packet buffer parameters overflow: offset: 0x%" PRIx32
303 ", size: %" PRIu32,
304 name, buf->offset, buf->size);
305 return false;
306 }
307
308 /* Check that the buffers are contained within the allocated space */
309 if (buf->offset + buf->size > LPC_WIN_SIZE) {
310 mctp_prerr(
311 "%s packet buffer parameters exceed %uM window size: offset: 0x%" PRIx32
312 ", size: %" PRIu32,
313 name, (LPC_WIN_SIZE / (1024 * 1024)), buf->offset,
314 buf->size);
315 return false;
316 }
317
318 /* Check that the baseline transmission unit is supported */
319 if (buf->size < ASTLPC_PACKET_SIZE(MCTP_PACKET_SIZE(MCTP_BTU))) {
320 mctp_prerr(
321 "%s packet buffer too small: Require %zu bytes to support the %u byte baseline transmission unit, found %" PRIu32,
322 name, ASTLPC_PACKET_SIZE(MCTP_PACKET_SIZE(MCTP_BTU)),
323 MCTP_BTU, buf->size);
324 return false;
325 }
326
327 /* Check for overlap with the control space */
328 if (buf->offset < control_size) {
329 mctp_prerr(
330 "%s packet buffer overlaps control region {0x%" PRIx32
331 ", %" PRIu32 "}: Rx {0x%" PRIx32 ", %" PRIu32 "}",
332 name, 0U, control_size, buf->offset, buf->size);
333 return false;
334 }
335
336 return true;
337}
338
339static bool mctp_astlpc_layout_validate(struct mctp_astlpc_layout *layout)
340{
341 struct mctp_astlpc_buffer *rx = &layout->rx;
342 struct mctp_astlpc_buffer *tx = &layout->tx;
343 bool rx_valid, tx_valid;
344
345 rx_valid = mctp_astlpc_buffer_validate(rx, "Rx");
346 tx_valid = mctp_astlpc_buffer_validate(tx, "Tx");
347
348 if (!(rx_valid && tx_valid))
349 return false;
350
351 /* Check that the buffers are disjoint */
352 if ((rx->offset <= tx->offset && rx->offset + rx->size > tx->offset) ||
353 (tx->offset <= rx->offset && tx->offset + tx->size > rx->offset)) {
354 mctp_prerr("Rx and Tx packet buffers overlap: Rx {0x%" PRIx32
355 ", %" PRIu32 "}, Tx {0x%" PRIx32 ", %" PRIu32 "}",
356 rx->offset, rx->size, tx->offset, tx->size);
357 return false;
358 }
359
360 return true;
361}
362
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930363static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc)
364{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930365 struct mctp_lpcmap_hdr hdr = { 0 };
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930366 uint8_t status;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930367 size_t sz;
368
369 /*
370 * The largest buffer size is half of the allocated MCTP space
371 * excluding the control space.
372 */
373 sz = ((LPC_WIN_SIZE - control_size) / 2);
374
375 /*
376 * Trim the MTU to a multiple of 16 to meet the requirements of 12.17
377 * Query Hop in DSP0236 v1.3.0.
378 */
379 sz = MCTP_BODY_SIZE(ASTLPC_BODY_SIZE(sz));
380 sz &= ~0xfUL;
381 sz = ASTLPC_PACKET_SIZE(MCTP_PACKET_SIZE(sz));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930382
Andrew Jefferya9368982020-06-09 13:07:39 +0930383 if (astlpc->requested_mtu) {
384 size_t r;
385
386 r = ASTLPC_PACKET_SIZE(MCTP_PACKET_SIZE(astlpc->requested_mtu));
387 sz = MIN(sz, r);
388 }
389
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930390 /* Flip the buffers as the names are defined in terms of the host */
Andrew Jeffery3a540662020-05-26 19:55:30 +0930391 astlpc->layout.tx.offset = control_size;
392 astlpc->layout.tx.size = sz;
393 astlpc->layout.rx.offset =
394 astlpc->layout.tx.offset + astlpc->layout.tx.size;
395 astlpc->layout.rx.size = sz;
396
Andrew Jefferya9368982020-06-09 13:07:39 +0930397 if (!mctp_astlpc_layout_validate(&astlpc->layout)) {
398 astlpc_prerr(astlpc, "Cannot support an MTU of %zu", sz);
399 return -EINVAL;
400 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930401
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930402 hdr = (struct mctp_lpcmap_hdr){
403 .magic = htobe32(ASTLPC_MCTP_MAGIC),
404 .bmc_ver_min = htobe16(ASTLPC_VER_MIN),
405 .bmc_ver_cur = htobe16(ASTLPC_VER_CUR),
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930406
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930407 /* Flip the buffers back as we're now describing the host's
408 * configuration to the host */
Andrew Jeffery3a540662020-05-26 19:55:30 +0930409 .layout.rx_offset = htobe32(astlpc->layout.tx.offset),
410 .layout.rx_size = htobe32(astlpc->layout.tx.size),
411 .layout.tx_offset = htobe32(astlpc->layout.rx.offset),
412 .layout.tx_size = htobe32(astlpc->layout.rx.size),
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930413 };
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930414
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930415 mctp_astlpc_lpc_write(astlpc, &hdr, 0, sizeof(hdr));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930416
417 /* set status indicating that the BMC is now active */
418 status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930419 return mctp_astlpc_kcs_set_status(astlpc, status);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930420}
421
422static int mctp_binding_astlpc_start_bmc(struct mctp_binding *b)
423{
424 struct mctp_binding_astlpc *astlpc =
425 container_of(b, struct mctp_binding_astlpc, binding);
426
427 return mctp_astlpc_init_bmc(astlpc);
428}
429
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930430static bool mctp_astlpc_validate_version(uint16_t bmc_ver_min,
431 uint16_t bmc_ver_cur,
432 uint16_t host_ver_min,
433 uint16_t host_ver_cur)
434{
435 if (!(bmc_ver_min && bmc_ver_cur && host_ver_min && host_ver_cur)) {
436 mctp_prerr("Invalid version present in [%" PRIu16 ", %" PRIu16
437 "], [%" PRIu16 ", %" PRIu16 "]",
438 bmc_ver_min, bmc_ver_cur, host_ver_min,
439 host_ver_cur);
440 return false;
441 } else if (bmc_ver_min > bmc_ver_cur) {
442 mctp_prerr("Invalid bmc version range [%" PRIu16 ", %" PRIu16
443 "]",
444 bmc_ver_min, bmc_ver_cur);
445 return false;
446 } else if (host_ver_min > host_ver_cur) {
447 mctp_prerr("Invalid host version range [%" PRIu16 ", %" PRIu16
448 "]",
449 host_ver_min, host_ver_cur);
450 return false;
451 } else if ((host_ver_cur < bmc_ver_min) ||
452 (host_ver_min > bmc_ver_cur)) {
453 mctp_prerr(
454 "Unable to satisfy version negotiation with ranges [%" PRIu16
455 ", %" PRIu16 "] and [%" PRIu16 ", %" PRIu16 "]",
456 bmc_ver_min, bmc_ver_cur, host_ver_min, host_ver_cur);
457 return false;
458 }
459
460 return true;
461}
462
Andrew Jeffery3a540662020-05-26 19:55:30 +0930463static int mctp_astlpc_negotiate_layout_host(struct mctp_binding_astlpc *astlpc)
464{
465 struct mctp_astlpc_layout layout;
466 uint32_t sz;
467 int rc;
468
469 rc = mctp_astlpc_layout_read(astlpc, &layout);
470 if (rc < 0)
471 return rc;
472
473 if (!mctp_astlpc_layout_validate(&layout)) {
474 astlpc_prerr(
475 astlpc,
476 "BMC provided invalid buffer layout: Rx {0x%" PRIx32
477 ", %" PRIu32 "}, Tx {0x%" PRIx32 ", %" PRIu32 "}",
478 layout.rx.offset, layout.rx.size, layout.tx.offset,
479 layout.tx.size);
480 return -EINVAL;
481 }
482
Andrew Jefferya9368982020-06-09 13:07:39 +0930483 astlpc_prinfo(astlpc, "Desire an MTU of %" PRIu32 " bytes",
484 astlpc->requested_mtu);
485
486 sz = ASTLPC_PACKET_SIZE(MCTP_PACKET_SIZE(astlpc->requested_mtu));
Andrew Jeffery3a540662020-05-26 19:55:30 +0930487 layout.rx.size = sz;
488
489 if (!mctp_astlpc_layout_validate(&layout)) {
490 astlpc_prerr(
491 astlpc,
492 "Generated invalid buffer layout with size %" PRIu32
493 ": Rx {0x%" PRIx32 ", %" PRIu32 "}, Tx {0x%" PRIx32
494 ", %" PRIu32 "}",
495 sz, layout.rx.offset, layout.rx.size, layout.tx.offset,
496 layout.tx.size);
497 return -EINVAL;
498 }
499
Andrew Jefferya9368982020-06-09 13:07:39 +0930500 astlpc_prinfo(astlpc, "Requesting MTU of %" PRIu32 " bytes",
501 astlpc->requested_mtu);
Andrew Jeffery3a540662020-05-26 19:55:30 +0930502
503 return mctp_astlpc_layout_write(astlpc, &layout);
504}
505
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930506static int mctp_astlpc_init_host(struct mctp_binding_astlpc *astlpc)
507{
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930508 const uint16_t ver_min_be = htobe16(ASTLPC_VER_MIN);
509 const uint16_t ver_cur_be = htobe16(ASTLPC_VER_CUR);
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930510 uint16_t bmc_ver_min, bmc_ver_cur;
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930511 struct mctp_lpcmap_hdr hdr;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930512 uint8_t status;
513 int rc;
514
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930515 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930516 if (rc) {
517 mctp_prwarn("KCS status read failed");
518 return rc;
519 }
520
521 astlpc->kcs_status = status;
522
523 if (!(status & KCS_STATUS_BMC_READY))
524 return -EHOSTDOWN;
525
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930526 mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930527
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930528 bmc_ver_min = be16toh(hdr.bmc_ver_min);
529 bmc_ver_cur = be16toh(hdr.bmc_ver_cur);
530
531 if (!mctp_astlpc_validate_version(bmc_ver_min, bmc_ver_cur,
532 ASTLPC_VER_MIN, ASTLPC_VER_CUR)) {
533 astlpc_prerr(astlpc, "Cannot negotiate with invalid versions");
534 return -EINVAL;
535 }
536
Andrew Jeffery3a540662020-05-26 19:55:30 +0930537 /*
538 * Negotation always chooses the highest protocol version that
539 * satisfies the version constraints. So check whether the BMC supports
540 * v2, and if so, negotiate in v2 style.
541 */
542 if (ASTLPC_VER_CUR >= 2 && bmc_ver_cur >= 2) {
543 rc = mctp_astlpc_negotiate_layout_host(astlpc);
544 if (rc < 0)
545 return rc;
546 }
547
548 /* Version negotiation */
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930549 mctp_astlpc_lpc_write(astlpc, &ver_min_be,
550 offsetof(struct mctp_lpcmap_hdr, host_ver_min),
551 sizeof(ver_min_be));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930552
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930553 mctp_astlpc_lpc_write(astlpc, &ver_cur_be,
554 offsetof(struct mctp_lpcmap_hdr, host_ver_cur),
555 sizeof(ver_cur_be));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930556
557 /* Send channel init command */
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930558 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, 0x0);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930559 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930560 astlpc_prwarn(astlpc, "KCS write failed");
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930561 }
562
563 return rc;
564}
565
566static int mctp_binding_astlpc_start_host(struct mctp_binding *b)
567{
568 struct mctp_binding_astlpc *astlpc =
569 container_of(b, struct mctp_binding_astlpc, binding);
570
571 return mctp_astlpc_init_host(astlpc);
572}
573
574static bool __mctp_astlpc_kcs_ready(struct mctp_binding_astlpc *astlpc,
575 uint8_t status, bool is_write)
576{
577 bool is_bmc;
578 bool ready_state;
579 uint8_t flag;
580
581 is_bmc = (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC);
582 flag = (is_bmc ^ is_write) ? KCS_STATUS_IBF : KCS_STATUS_OBF;
583 ready_state = is_write ? 0 : 1;
584
585 return !!(status & flag) == ready_state;
586}
587
588static inline bool
589mctp_astlpc_kcs_read_ready(struct mctp_binding_astlpc *astlpc, uint8_t status)
590{
591 return __mctp_astlpc_kcs_ready(astlpc, status, false);
592}
593
594static inline bool
595mctp_astlpc_kcs_write_ready(struct mctp_binding_astlpc *astlpc, uint8_t status)
596{
597 return __mctp_astlpc_kcs_ready(astlpc, status, true);
598}
599
Jeremy Kerr672c8852019-03-01 12:18:07 +0800600static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc,
601 uint8_t data)
602{
603 uint8_t status;
604 int rc;
605
606 for (;;) {
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930607 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS,
608 &status);
Andrew Jeffery1b27fe82020-01-24 16:05:11 +1030609 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930610 astlpc_prwarn(astlpc, "KCS status read failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +0800611 return -1;
612 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930613 if (mctp_astlpc_kcs_write_ready(astlpc, status))
Jeremy Kerr672c8852019-03-01 12:18:07 +0800614 break;
615 /* todo: timeout */
616 }
617
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930618 rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530619 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930620 astlpc_prwarn(astlpc, "KCS data write failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +0800621 return -1;
622 }
623
624 return 0;
625}
626
627static int mctp_binding_astlpc_tx(struct mctp_binding *b,
628 struct mctp_pktbuf *pkt)
629{
630 struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b);
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930631 uint32_t len, len_be;
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030632 struct mctp_hdr *hdr;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800633
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030634 hdr = mctp_pktbuf_hdr(pkt);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800635 len = mctp_pktbuf_size(pkt);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030636
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930637 astlpc_prdebug(astlpc,
638 "%s: Transmitting %" PRIu32
639 "-byte packet (%hhu, %hhu, 0x%hhx)",
640 __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030641
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930642 if (len > ASTLPC_BODY_SIZE(astlpc->layout.tx.size)) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930643 astlpc_prwarn(astlpc, "invalid TX len 0x%x", len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800644 return -1;
645 }
646
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930647 len_be = htobe32(len);
648 mctp_astlpc_lpc_write(astlpc, &len_be, astlpc->layout.tx.offset,
649 sizeof(len_be));
650 mctp_astlpc_lpc_write(astlpc, hdr, astlpc->layout.tx.offset + 4, len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800651
652 mctp_binding_set_tx_enabled(b, false);
653
654 mctp_astlpc_kcs_send(astlpc, 0x1);
655 return 0;
656}
657
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930658static uint16_t mctp_astlpc_negotiate_version(uint16_t bmc_ver_min,
659 uint16_t bmc_ver_cur,
660 uint16_t host_ver_min,
661 uint16_t host_ver_cur)
662{
663 if (!mctp_astlpc_validate_version(bmc_ver_min, bmc_ver_cur,
664 host_ver_min, host_ver_cur))
665 return ASTLPC_VER_BAD;
666
667 if (bmc_ver_cur < host_ver_cur)
668 return bmc_ver_cur;
669
670 return host_ver_cur;
671}
672
Andrew Jeffery3a540662020-05-26 19:55:30 +0930673static uint32_t mctp_astlpc_calculate_mtu(struct mctp_binding_astlpc *astlpc,
674 struct mctp_astlpc_layout *layout)
675{
676 uint32_t low, high, limit;
677
678 /* Derive the largest MTU the BMC _can_ support */
679 low = MIN(astlpc->layout.rx.offset, astlpc->layout.tx.offset);
680 high = MAX(astlpc->layout.rx.offset, astlpc->layout.tx.offset);
681 limit = high - low;
682
Andrew Jefferya9368982020-06-09 13:07:39 +0930683 /* Determine the largest MTU the BMC _wants_ to support */
684 if (astlpc->requested_mtu) {
685 uint32_t req = astlpc->requested_mtu;
686
687 limit = MIN(limit, ASTLPC_PACKET_SIZE(MCTP_PACKET_SIZE(req)));
688 }
Andrew Jeffery3a540662020-05-26 19:55:30 +0930689
690 /* Determine the accepted MTU, applied both directions by convention */
691 return MCTP_BODY_SIZE(ASTLPC_BODY_SIZE(MIN(limit, layout->tx.size)));
692}
693
694static int mctp_astlpc_negotiate_layout_bmc(struct mctp_binding_astlpc *astlpc)
695{
696 struct mctp_astlpc_layout proposed, pending;
697 uint32_t sz, mtu;
698 int rc;
699
700 /* Extract the host's proposed layout */
701 rc = mctp_astlpc_layout_read(astlpc, &proposed);
702 if (rc < 0)
703 return rc;
704
705 if (!mctp_astlpc_layout_validate(&proposed))
706 return -EINVAL;
707
708 /* Negotiate the MTU */
709 mtu = mctp_astlpc_calculate_mtu(astlpc, &proposed);
710 sz = ASTLPC_PACKET_SIZE(MCTP_PACKET_SIZE(mtu));
711
712 /*
713 * Use symmetric MTUs by convention and to pass constraints in rx/tx
714 * functions
715 */
716 pending = astlpc->layout;
717 pending.tx.size = sz;
718 pending.rx.size = sz;
719
720 if (mctp_astlpc_layout_validate(&pending)) {
721 /* We found a sensible Rx MTU, so honour it */
722 astlpc->layout = pending;
723
724 /* Enforce the negotiated MTU */
725 rc = mctp_astlpc_layout_write(astlpc, &astlpc->layout);
726 if (rc < 0)
727 return rc;
728
729 astlpc_prinfo(astlpc, "Negotiated an MTU of %" PRIu32 " bytes",
730 mtu);
731 } else {
732 astlpc_prwarn(astlpc, "MTU negotiation failed");
733 return -EINVAL;
734 }
735
736 if (astlpc->version >= 2)
737 astlpc->binding.pkt_size = MCTP_PACKET_SIZE(mtu);
738
739 return 0;
740}
741
Jeremy Kerr672c8852019-03-01 12:18:07 +0800742static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc)
743{
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930744 uint16_t negotiated, negotiated_be;
745 struct mctp_lpcmap_hdr hdr;
746 uint8_t status;
Andrew Jeffery3a540662020-05-26 19:55:30 +0930747 int rc;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930748
749 mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr));
750
751 /* Version negotiation */
752 negotiated =
753 mctp_astlpc_negotiate_version(ASTLPC_VER_MIN, ASTLPC_VER_CUR,
754 be16toh(hdr.host_ver_min),
755 be16toh(hdr.host_ver_cur));
756
Andrew Jeffery3a540662020-05-26 19:55:30 +0930757 /* Host Rx MTU negotiation: Failure terminates channel init */
758 rc = mctp_astlpc_negotiate_layout_bmc(astlpc);
759 if (rc < 0)
760 negotiated = ASTLPC_VER_BAD;
761
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930762 /* Populate the negotiated version */
763 astlpc->version = negotiated;
764 negotiated_be = htobe16(negotiated);
765 mctp_astlpc_lpc_write(astlpc, &negotiated_be,
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930766 offsetof(struct mctp_lpcmap_hdr, negotiated_ver),
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930767 sizeof(negotiated_be));
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930768
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930769 /* Finalise the configuration */
770 status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
771 if (negotiated > 0) {
772 astlpc_prinfo(astlpc, "Negotiated binding version %" PRIu16,
773 negotiated);
774 status |= KCS_STATUS_CHANNEL_ACTIVE;
775 } else {
776 astlpc_prerr(astlpc, "Failed to initialise channel\n");
777 }
Jeremy Kerr672c8852019-03-01 12:18:07 +0800778
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930779 mctp_astlpc_kcs_set_status(astlpc, status);
780
781 mctp_binding_set_tx_enabled(&astlpc->binding,
782 status & KCS_STATUS_CHANNEL_ACTIVE);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800783}
784
785static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
786{
787 struct mctp_pktbuf *pkt;
788 uint32_t len;
789
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930790 mctp_astlpc_lpc_read(astlpc, &len, astlpc->layout.rx.offset,
791 sizeof(len));
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530792 len = be32toh(len);
793
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930794 if (len > ASTLPC_BODY_SIZE(astlpc->layout.rx.size)) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930795 astlpc_prwarn(astlpc, "invalid RX len 0x%x", len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800796 return;
797 }
798
Andrew Jefferyb93b6112020-06-05 14:13:44 +0930799 assert(astlpc->binding.pkt_size >= 0);
800 if (len > (uint32_t)astlpc->binding.pkt_size) {
Jeremy Kerr672c8852019-03-01 12:18:07 +0800801 mctp_prwarn("invalid RX len 0x%x", len);
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930802 astlpc_prwarn(astlpc, "invalid RX len 0x%x", len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800803 return;
804 }
805
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800806 pkt = mctp_pktbuf_alloc(&astlpc->binding, len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800807 if (!pkt)
808 goto out_complete;
809
Andrew Jeffery55fb90b2020-05-12 13:54:37 +0930810 mctp_astlpc_lpc_read(astlpc, mctp_pktbuf_hdr(pkt),
811 astlpc->layout.rx.offset + 4, len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800812
813 mctp_bus_rx(&astlpc->binding, pkt);
814
815out_complete:
816 mctp_astlpc_kcs_send(astlpc, 0x2);
817}
818
819static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc)
820{
821 mctp_binding_set_tx_enabled(&astlpc->binding, true);
822}
823
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930824static int mctp_astlpc_finalise_channel(struct mctp_binding_astlpc *astlpc)
825{
Andrew Jeffery3a540662020-05-26 19:55:30 +0930826 struct mctp_astlpc_layout layout;
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930827 uint16_t negotiated;
828 int rc;
829
830 rc = mctp_astlpc_lpc_read(astlpc, &negotiated,
831 offsetof(struct mctp_lpcmap_hdr,
832 negotiated_ver),
833 sizeof(negotiated));
834 if (rc < 0)
835 return rc;
836
837 negotiated = be16toh(negotiated);
838
839 if (negotiated == ASTLPC_VER_BAD || negotiated < ASTLPC_VER_MIN ||
840 negotiated > ASTLPC_VER_CUR) {
841 astlpc_prerr(astlpc, "Failed to negotiate version, got: %u\n",
842 negotiated);
843 return -EINVAL;
844 }
845
846 astlpc->version = negotiated;
847
Andrew Jeffery3a540662020-05-26 19:55:30 +0930848 rc = mctp_astlpc_layout_read(astlpc, &layout);
849 if (rc < 0)
850 return rc;
851
852 if (!mctp_astlpc_layout_validate(&layout)) {
853 mctp_prerr("BMC proposed invalid buffer parameters");
854 return -EINVAL;
855 }
856
857 astlpc->layout = layout;
858
859 if (negotiated >= 2)
860 astlpc->binding.pkt_size =
861 ASTLPC_BODY_SIZE(astlpc->layout.tx.size);
862
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930863 return 0;
864}
865
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930866static int mctp_astlpc_update_channel(struct mctp_binding_astlpc *astlpc,
867 uint8_t status)
868{
869 uint8_t updated;
870 int rc = 0;
871
872 assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST);
873
874 updated = astlpc->kcs_status ^ status;
875
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930876 astlpc_prdebug(astlpc, "%s: status: 0x%x, update: 0x%x", __func__,
877 status, updated);
878
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930879 if (updated & KCS_STATUS_BMC_READY) {
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930880 if (status & KCS_STATUS_BMC_READY) {
881 astlpc->kcs_status = status;
882 return astlpc->binding.start(&astlpc->binding);
883 } else {
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930884 mctp_binding_set_tx_enabled(&astlpc->binding, false);
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930885 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930886 }
887
Andrew Jeffery4e8264b2020-05-23 20:34:33 +0930888 if (astlpc->version == 0 || updated & KCS_STATUS_CHANNEL_ACTIVE) {
889 bool enable;
890
891 rc = mctp_astlpc_finalise_channel(astlpc);
892 enable = (status & KCS_STATUS_CHANNEL_ACTIVE) && rc == 0;
893
894 mctp_binding_set_tx_enabled(&astlpc->binding, enable);
895 }
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930896
897 astlpc->kcs_status = status;
898
899 return rc;
900}
901
Jeremy Kerr672c8852019-03-01 12:18:07 +0800902int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
903{
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530904 uint8_t status, data;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800905 int rc;
906
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930907 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530908 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930909 astlpc_prwarn(astlpc, "KCS read error");
Jeremy Kerr672c8852019-03-01 12:18:07 +0800910 return -1;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800911 }
912
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930913 astlpc_prdebug(astlpc, "%s: status: 0x%hhx", __func__, status);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030914
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930915 if (!mctp_astlpc_kcs_read_ready(astlpc, status))
Jeremy Kerr672c8852019-03-01 12:18:07 +0800916 return 0;
917
Andrew Jefferyf13cb972020-05-28 09:30:09 +0930918 rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_DATA, &data);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530919 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930920 astlpc_prwarn(astlpc, "KCS data read error");
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530921 return -1;
922 }
923
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930924 astlpc_prdebug(astlpc, "%s: data: 0x%hhx", __func__, data);
Andrew Jefferyedfe3832020-02-06 11:52:11 +1030925
Jeremy Kerr672c8852019-03-01 12:18:07 +0800926 switch (data) {
927 case 0x0:
928 mctp_astlpc_init_channel(astlpc);
929 break;
930 case 0x1:
931 mctp_astlpc_rx_start(astlpc);
932 break;
933 case 0x2:
934 mctp_astlpc_tx_complete(astlpc);
935 break;
Jeremy Kerr1a4b55a2019-06-24 14:22:39 +0800936 case 0xff:
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930937 /* No responsibilities for the BMC on 0xff */
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930938 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) {
939 rc = mctp_astlpc_update_channel(astlpc, status);
940 if (rc < 0)
941 return rc;
942 }
943 break;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800944 default:
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930945 astlpc_prwarn(astlpc, "unknown message 0x%x", data);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800946 }
Andrew Jefferyd0f5da02020-05-28 09:12:55 +0930947
948 /* Handle silent loss of bmc-ready */
949 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) {
950 if (!(status & KCS_STATUS_BMC_READY && data == 0xff))
951 return mctp_astlpc_update_channel(astlpc, status);
952 }
953
954 return rc;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800955}
956
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530957/* allocate and basic initialisation */
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930958static struct mctp_binding_astlpc *__mctp_astlpc_init(uint8_t mode,
959 uint32_t mtu)
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530960{
961 struct mctp_binding_astlpc *astlpc;
962
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930963 assert((mode == MCTP_BINDING_ASTLPC_MODE_BMC) ||
964 (mode == MCTP_BINDING_ASTLPC_MODE_HOST));
965
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530966 astlpc = __mctp_alloc(sizeof(*astlpc));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930967 if (!astlpc)
968 return NULL;
969
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530970 memset(astlpc, 0, sizeof(*astlpc));
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930971 astlpc->mode = mode;
972 astlpc->lpc_map = NULL;
Andrew Jefferya9368982020-06-09 13:07:39 +0930973 astlpc->requested_mtu = mtu;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530974 astlpc->binding.name = "astlpc";
975 astlpc->binding.version = 1;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930976 astlpc->binding.pkt_size = MCTP_PACKET_SIZE(mtu);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530977 astlpc->binding.pkt_pad = 0;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930978 astlpc->binding.tx = mctp_binding_astlpc_tx;
979 if (mode == MCTP_BINDING_ASTLPC_MODE_BMC)
980 astlpc->binding.start = mctp_binding_astlpc_start_bmc;
981 else if (mode == MCTP_BINDING_ASTLPC_MODE_HOST)
982 astlpc->binding.start = mctp_binding_astlpc_start_host;
983 else {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +0930984 astlpc_prerr(astlpc, "%s: Invalid mode: %d\n", __func__, mode);
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930985 __mctp_free(astlpc);
986 return NULL;
987 }
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530988
989 return astlpc;
990}
991
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800992struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b)
993{
994 return &b->binding;
995}
996
Andrew Jeffery7cd72f12020-05-12 20:27:59 +0930997struct mctp_binding_astlpc *
998mctp_astlpc_init(uint8_t mode, uint32_t mtu, void *lpc_map,
999 const struct mctp_binding_astlpc_ops *ops, void *ops_data)
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301000{
1001 struct mctp_binding_astlpc *astlpc;
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301002
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301003 if (!(mode == MCTP_BINDING_ASTLPC_MODE_BMC ||
1004 mode == MCTP_BINDING_ASTLPC_MODE_HOST)) {
1005 mctp_prerr("Unknown binding mode: %u", mode);
1006 return NULL;
1007 }
1008
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301009 astlpc = __mctp_astlpc_init(mode, mtu);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301010 if (!astlpc)
1011 return NULL;
1012
1013 memcpy(&astlpc->ops, ops, sizeof(astlpc->ops));
1014 astlpc->ops_data = ops_data;
1015 astlpc->lpc_map = lpc_map;
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301016 astlpc->mode = mode;
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301017
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301018 return astlpc;
1019}
1020
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301021struct mctp_binding_astlpc *
1022mctp_astlpc_init_ops(const struct mctp_binding_astlpc_ops *ops, void *ops_data,
1023 void *lpc_map)
1024{
1025 return mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, MCTP_BTU, lpc_map,
1026 ops, ops_data);
1027}
1028
Andrew Jeffery4663f672020-03-10 23:36:24 +10301029void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc)
1030{
Andrew Jefferyd0f5da02020-05-28 09:12:55 +09301031 /* Clear channel-active and bmc-ready */
1032 if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC)
1033 mctp_astlpc_kcs_set_status(astlpc, KCS_STATUS_OBF);
Andrew Jeffery4663f672020-03-10 23:36:24 +10301034 __mctp_free(astlpc);
1035}
1036
Jeremy Kerrb214c642019-11-27 11:34:00 +08001037#ifdef MCTP_HAVE_FILEIO
Andrew Jeffery55fb90b2020-05-12 13:54:37 +09301038
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301039static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001040{
1041 struct aspeed_lpc_ctrl_mapping map = {
1042 .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
1043 .window_id = 0, /* There's only one */
1044 .flags = 0,
1045 .addr = 0,
1046 .offset = 0,
1047 .size = 0
1048 };
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301049 void *lpc_map_base;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001050 int fd, rc;
1051
1052 fd = open(lpc_path, O_RDWR | O_SYNC);
1053 if (fd < 0) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301054 astlpc_prwarn(astlpc, "LPC open (%s) failed", lpc_path);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001055 return -1;
1056 }
1057
1058 rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map);
1059 if (rc) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301060 astlpc_prwarn(astlpc, "LPC GET_SIZE failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +08001061 close(fd);
1062 return -1;
1063 }
1064
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301065 lpc_map_base =
1066 mmap(NULL, map.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1067 if (lpc_map_base == MAP_FAILED) {
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301068 astlpc_prwarn(astlpc, "LPC mmap failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +08001069 rc = -1;
1070 } else {
Andrew Jeffery979c6a12020-05-23 20:04:49 +09301071 astlpc->lpc_map = lpc_map_base + map.size - LPC_WIN_SIZE;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001072 }
1073
1074 close(fd);
1075
1076 return rc;
1077}
1078
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301079static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001080{
1081 astlpc->kcs_fd = open(kcs_path, O_RDWR);
1082 if (astlpc->kcs_fd < 0)
1083 return -1;
1084
1085 return 0;
1086}
1087
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301088static int __mctp_astlpc_fileio_kcs_read(void *arg,
1089 enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val)
1090{
1091 struct mctp_binding_astlpc *astlpc = arg;
1092 off_t offset = reg;
1093 int rc;
1094
1095 rc = pread(astlpc->kcs_fd, val, 1, offset);
1096
1097 return rc == 1 ? 0 : -1;
1098}
1099
1100static int __mctp_astlpc_fileio_kcs_write(void *arg,
1101 enum mctp_binding_astlpc_kcs_reg reg, uint8_t val)
1102{
1103 struct mctp_binding_astlpc *astlpc = arg;
1104 off_t offset = reg;
1105 int rc;
1106
1107 rc = pwrite(astlpc->kcs_fd, &val, 1, offset);
1108
1109 return rc == 1 ? 0 : -1;
1110}
1111
1112int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc)
1113{
1114 return astlpc->kcs_fd;
1115}
1116
1117struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void)
Jeremy Kerr672c8852019-03-01 12:18:07 +08001118{
1119 struct mctp_binding_astlpc *astlpc;
1120 int rc;
1121
Andrew Jeffery7cd72f12020-05-12 20:27:59 +09301122 /*
1123 * If we're doing file IO then we're very likely not running
1124 * freestanding, so lets assume that we're on the BMC side
1125 */
1126 astlpc = __mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, MCTP_BTU);
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301127 if (!astlpc)
1128 return NULL;
Jeremy Kerr672c8852019-03-01 12:18:07 +08001129
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301130 /* Set internal operations for kcs. We use direct accesses to the lpc
1131 * map area */
1132 astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read;
1133 astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write;
1134 astlpc->ops_data = astlpc;
1135
1136 rc = mctp_astlpc_init_fileio_lpc(astlpc);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001137 if (rc) {
1138 free(astlpc);
1139 return NULL;
1140 }
1141
Jeremy Kerrbc53d352019-08-28 14:26:14 +05301142 rc = mctp_astlpc_init_fileio_kcs(astlpc);
Jeremy Kerr672c8852019-03-01 12:18:07 +08001143 if (rc) {
1144 free(astlpc);
1145 return NULL;
1146 }
1147
Jeremy Kerr672c8852019-03-01 12:18:07 +08001148 return astlpc;
1149}
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301150#else
1151struct mctp_binding_astlpc * __attribute__((const))
1152 mctp_astlpc_init_fileio(void)
1153{
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301154 astlpc_prerr(astlpc, "Missing support for file IO");
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301155 return NULL;
1156}
Jeremy Kerr672c8852019-03-01 12:18:07 +08001157
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301158int __attribute__((const)) mctp_astlpc_get_fd(
1159 struct mctp_binding_astlpc *astlpc __attribute__((unused)))
1160{
Andrew Jeffery9101a2a2020-05-22 16:08:03 +09301161 astlpc_prerr(astlpc, "Missing support for file IO");
Jeremy Kerr92a10a62019-08-28 16:55:54 +05301162 return -1;
1163}
1164#endif