blob: 6a5c436e429034a8b66213903a8cc1c6bb63af3e [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
3#include <assert.h>
Jeremy Kerr92a10a62019-08-28 16:55:54 +05304#include <endian.h>
Andrew Jeffery59c6a5c2020-01-17 15:52:51 +10305#include <err.h>
Jeremy Kerr672c8852019-03-01 12:18:07 +08006#include <stdbool.h>
7#include <stdlib.h>
8#include <string.h>
Jeremy Kerr672c8852019-03-01 12:18:07 +08009
Jeremy Kerr672c8852019-03-01 12:18:07 +080010#define pr_fmt(x) "astlpc: " x
11
Andrew Jeffery59c6a5c2020-01-17 15:52:51 +103012#if HAVE_CONFIG_H
13#include "config.h"
14#endif
15
Jeremy Kerr672c8852019-03-01 12:18:07 +080016#include "libmctp.h"
17#include "libmctp-alloc.h"
18#include "libmctp-log.h"
19#include "libmctp-astlpc.h"
20
Jeremy Kerrb214c642019-11-27 11:34:00 +080021#ifdef MCTP_HAVE_FILEIO
Jeremy Kerr92a10a62019-08-28 16:55:54 +053022
Jeremy Kerrc6f676d2019-12-19 09:24:06 +080023#include <unistd.h>
Jeremy Kerr92a10a62019-08-28 16:55:54 +053024#include <fcntl.h>
25#include <sys/ioctl.h>
26#include <sys/mman.h>
27#include <linux/aspeed-lpc-ctrl.h>
28
29/* kernel interface */
30static const char *kcs_path = "/dev/mctp0";
31static const char *lpc_path = "/dev/aspeed-lpc-ctrl";
32
33#endif
34
Jeremy Kerr672c8852019-03-01 12:18:07 +080035struct mctp_binding_astlpc {
36 struct mctp_binding binding;
Jeremy Kerrbc53d352019-08-28 14:26:14 +053037
Jeremy Kerr672c8852019-03-01 12:18:07 +080038 union {
39 void *lpc_map;
40 struct mctp_lpcmap_hdr *lpc_hdr;
41 };
Jeremy Kerrbc53d352019-08-28 14:26:14 +053042
43 /* direct ops data */
44 struct mctp_binding_astlpc_ops ops;
45 void *ops_data;
46 struct mctp_lpcmap_hdr *priv_hdr;
47
48 /* fileio ops data */
49 void *lpc_map_base;
Jeremy Kerr672c8852019-03-01 12:18:07 +080050 int kcs_fd;
51 uint8_t kcs_status;
52
53 bool running;
54
55 /* temporary transmit buffer */
56 uint8_t txbuf[256];
57};
58
59#ifndef container_of
60#define container_of(ptr, type, member) \
Andrew Jeffery1e0af042020-01-10 15:58:28 +103061 (type *)((char *)(ptr) - (char *)&((type *)0)->member)
Jeremy Kerr672c8852019-03-01 12:18:07 +080062#endif
63
64#define binding_to_astlpc(b) \
65 container_of(b, struct mctp_binding_astlpc, binding)
66
67#define MCTP_MAGIC 0x4d435450
68#define BMC_VER_MIN 1
69#define BMC_VER_CUR 1
70
71struct mctp_lpcmap_hdr {
72 uint32_t magic;
73
74 uint16_t bmc_ver_min;
75 uint16_t bmc_ver_cur;
76 uint16_t host_ver_min;
77 uint16_t host_ver_cur;
78 uint16_t negotiated_ver;
79 uint16_t pad0;
80
81 uint32_t rx_offset;
82 uint32_t rx_size;
83 uint32_t tx_offset;
84 uint32_t tx_size;
85} __attribute__((packed));
86
87/* layout of TX/RX areas */
88static const uint32_t rx_offset = 0x100;
89static const uint32_t rx_size = 0x100;
90static const uint32_t tx_offset = 0x200;
91static const uint32_t tx_size = 0x100;
92
Jeremy Kerr672c8852019-03-01 12:18:07 +080093#define LPC_WIN_SIZE (1 * 1024 * 1024)
94
95enum {
96 KCS_REG_DATA = 0,
97 KCS_REG_STATUS = 1,
98};
99
100#define KCS_STATUS_BMC_READY 0x80
101#define KCS_STATUS_CHANNEL_ACTIVE 0x40
102#define KCS_STATUS_IBF 0x02
103#define KCS_STATUS_OBF 0x01
104
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530105static bool lpc_direct(struct mctp_binding_astlpc *astlpc)
106{
107 return astlpc->lpc_map != NULL;
108}
109
Jeremy Kerr672c8852019-03-01 12:18:07 +0800110static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc,
111 uint8_t status)
112{
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530113 uint8_t data;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800114 int rc;
115
Jeremy Kerr1a4b55a2019-06-24 14:22:39 +0800116 /* Since we're setting the status register, we want the other endpoint
117 * to be interrupted. However, some hardware may only raise a host-side
118 * interrupt on an ODR event.
119 * So, write a dummy value of 0xff to ODR, which will ensure that an
120 * interrupt is triggered, and can be ignored by the host.
121 */
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530122 data = 0xff;
123 status |= KCS_STATUS_OBF;
Jeremy Kerr1a4b55a2019-06-24 14:22:39 +0800124
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530125 rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA,
126 data);
127 if (rc) {
128 mctp_prwarn("KCS dummy data write failed");
129 return -1;
130 }
131
132
133 rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS,
134 status);
135 if (rc) {
Jeremy Kerr672c8852019-03-01 12:18:07 +0800136 mctp_prwarn("KCS status write failed");
137 return -1;
138 }
139 return 0;
140}
141
142static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc,
143 uint8_t data)
144{
145 uint8_t status;
146 int rc;
147
148 for (;;) {
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530149 rc = astlpc->ops.kcs_read(astlpc->ops_data,
150 MCTP_ASTLPC_KCS_REG_STATUS, &status);
Andrew Jeffery1b27fe82020-01-24 16:05:11 +1030151 if (rc) {
Andrew Jeffery182204e2020-01-23 17:02:38 +1030152 mctp_prwarn("KCS status read failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +0800153 return -1;
154 }
155 if (!(status & KCS_STATUS_OBF))
156 break;
157 /* todo: timeout */
158 }
159
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530160 rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA,
161 data);
162 if (rc) {
Jeremy Kerr672c8852019-03-01 12:18:07 +0800163 mctp_prwarn("KCS data write failed");
164 return -1;
165 }
166
167 return 0;
168}
169
170static int mctp_binding_astlpc_tx(struct mctp_binding *b,
171 struct mctp_pktbuf *pkt)
172{
173 struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b);
174 uint32_t len;
175
176 len = mctp_pktbuf_size(pkt);
177 if (len > rx_size - 4) {
178 mctp_prwarn("invalid TX len 0x%x", len);
179 return -1;
180 }
181
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530182 if (lpc_direct(astlpc)) {
183 *(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len);
184 memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt),
185 len);
186 } else {
187 uint32_t tmp = htobe32(len);
188 astlpc->ops.lpc_write(astlpc->ops_data, &tmp, rx_offset,
189 sizeof(tmp));
190 astlpc->ops.lpc_write(astlpc->ops_data, mctp_pktbuf_hdr(pkt),
191 rx_offset + 4, len);
192 }
Jeremy Kerr672c8852019-03-01 12:18:07 +0800193
194 mctp_binding_set_tx_enabled(b, false);
195
196 mctp_astlpc_kcs_send(astlpc, 0x1);
197 return 0;
198}
199
200static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc)
201{
202 /* todo: actual version negotiation */
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530203 if (lpc_direct(astlpc)) {
204 astlpc->lpc_hdr->negotiated_ver = htobe16(1);
205 } else {
206 uint16_t ver = htobe16(1);
207 astlpc->ops.lpc_write(astlpc->ops_data, &ver,
208 offsetof(struct mctp_lpcmap_hdr,
209 negotiated_ver),
210 sizeof(ver));
211 }
Jeremy Kerr672c8852019-03-01 12:18:07 +0800212 mctp_astlpc_kcs_set_status(astlpc,
213 KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE |
214 KCS_STATUS_OBF);
215
216 mctp_binding_set_tx_enabled(&astlpc->binding, true);
217}
218
219static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
220{
221 struct mctp_pktbuf *pkt;
222 uint32_t len;
223
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530224 if (lpc_direct(astlpc)) {
225 len = *(uint32_t *)(astlpc->lpc_map + tx_offset);
226 } else {
227 astlpc->ops.lpc_read(astlpc->ops_data, &len,
228 tx_offset, sizeof(len));
229 }
230 len = be32toh(len);
231
Jeremy Kerr672c8852019-03-01 12:18:07 +0800232 if (len > tx_size - 4) {
233 mctp_prwarn("invalid RX len 0x%x", len);
234 return;
235 }
236
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800237 if (len > astlpc->binding.pkt_size) {
Jeremy Kerr672c8852019-03-01 12:18:07 +0800238 mctp_prwarn("invalid RX len 0x%x", len);
239 return;
240 }
241
Jeremy Kerrdf15f7e2019-08-05 15:41:19 +0800242 pkt = mctp_pktbuf_alloc(&astlpc->binding, len);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800243 if (!pkt)
244 goto out_complete;
245
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530246 if (lpc_direct(astlpc)) {
247 memcpy(mctp_pktbuf_hdr(pkt),
248 astlpc->lpc_map + tx_offset + 4, len);
249 } else {
250 astlpc->ops.lpc_read(astlpc->ops_data, mctp_pktbuf_hdr(pkt),
251 tx_offset + 4, len);
252 }
Jeremy Kerr672c8852019-03-01 12:18:07 +0800253
254 mctp_bus_rx(&astlpc->binding, pkt);
255
256out_complete:
257 mctp_astlpc_kcs_send(astlpc, 0x2);
258}
259
260static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc)
261{
262 mctp_binding_set_tx_enabled(&astlpc->binding, true);
263}
264
265int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
266{
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530267 uint8_t status, data;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800268 int rc;
269
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530270 rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS,
271 &status);
272 if (rc) {
Jeremy Kerr672c8852019-03-01 12:18:07 +0800273 mctp_prwarn("KCS read error");
274 return -1;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800275 }
276
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530277 if (!(status & KCS_STATUS_IBF))
Jeremy Kerr672c8852019-03-01 12:18:07 +0800278 return 0;
279
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530280 rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA,
281 &data);
282 if (rc) {
283 mctp_prwarn("KCS data read error");
284 return -1;
285 }
286
Jeremy Kerr672c8852019-03-01 12:18:07 +0800287 switch (data) {
288 case 0x0:
289 mctp_astlpc_init_channel(astlpc);
290 break;
291 case 0x1:
292 mctp_astlpc_rx_start(astlpc);
293 break;
294 case 0x2:
295 mctp_astlpc_tx_complete(astlpc);
296 break;
Jeremy Kerr1a4b55a2019-06-24 14:22:39 +0800297 case 0xff:
298 /* reserved value for dummy data writes; do nothing */
299 break;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800300 default:
301 mctp_prwarn("unknown message 0x%x", data);
302 }
303 return 0;
304}
305
Jeremy Kerr672c8852019-03-01 12:18:07 +0800306static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc)
307{
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530308 struct mctp_lpcmap_hdr *hdr;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800309 uint8_t status;
310 int rc;
311
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530312 if (lpc_direct(astlpc))
313 hdr = astlpc->lpc_hdr;
314 else
315 hdr = astlpc->priv_hdr;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800316
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530317 hdr->magic = htobe32(MCTP_MAGIC);
318 hdr->bmc_ver_min = htobe16(BMC_VER_MIN);
319 hdr->bmc_ver_cur = htobe16(BMC_VER_CUR);
320
321 hdr->rx_offset = htobe32(rx_offset);
322 hdr->rx_size = htobe32(rx_size);
323 hdr->tx_offset = htobe32(tx_offset);
324 hdr->tx_size = htobe32(tx_size);
325
326 if (!lpc_direct(astlpc))
327 astlpc->ops.lpc_write(astlpc->ops_data, hdr, 0, sizeof(*hdr));
Jeremy Kerr672c8852019-03-01 12:18:07 +0800328
329 /* set status indicating that the BMC is now active */
330 status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530331 rc = astlpc->ops.kcs_write(astlpc->ops_data,
332 MCTP_ASTLPC_KCS_REG_STATUS, status);
333 if (rc) {
Jeremy Kerr672c8852019-03-01 12:18:07 +0800334 mctp_prwarn("KCS write failed");
Jeremy Kerr672c8852019-03-01 12:18:07 +0800335 }
336
337 return rc;
338}
339
Jeremy Kerr8081beb2019-09-04 12:33:00 +0800340static int mctp_binding_astlpc_start(struct mctp_binding *b)
341{
342 struct mctp_binding_astlpc *astlpc = container_of(b,
343 struct mctp_binding_astlpc, binding);
344
345 return mctp_astlpc_init_bmc(astlpc);
346}
347
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530348/* allocate and basic initialisation */
349static struct mctp_binding_astlpc *__mctp_astlpc_init(void)
350{
351 struct mctp_binding_astlpc *astlpc;
352
353 astlpc = __mctp_alloc(sizeof(*astlpc));
354 memset(astlpc, 0, sizeof(*astlpc));
355 astlpc->binding.name = "astlpc";
356 astlpc->binding.version = 1;
357 astlpc->binding.tx = mctp_binding_astlpc_tx;
Jeremy Kerr8081beb2019-09-04 12:33:00 +0800358 astlpc->binding.start = mctp_binding_astlpc_start;
Andrew Jeffery73c268e2020-01-30 10:16:09 +1030359 astlpc->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU);
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530360 astlpc->binding.pkt_pad = 0;
361 astlpc->lpc_map = NULL;
362
363 return astlpc;
364}
365
Jeremy Kerr3b36d172019-09-04 11:56:09 +0800366struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b)
367{
368 return &b->binding;
369}
370
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530371struct mctp_binding_astlpc *mctp_astlpc_init_ops(
Andrew Jefferya0452492020-02-06 11:47:39 +1030372 const struct mctp_binding_astlpc_ops *ops,
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530373 void *ops_data, void *lpc_map)
374{
375 struct mctp_binding_astlpc *astlpc;
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530376
377 astlpc = __mctp_astlpc_init();
378 if (!astlpc)
379 return NULL;
380
381 memcpy(&astlpc->ops, ops, sizeof(astlpc->ops));
382 astlpc->ops_data = ops_data;
383 astlpc->lpc_map = lpc_map;
384
385 /* In indirect mode, we keep a separate buffer of header data.
386 * We need to sync this through the lpc_read/lpc_write ops.
387 */
388 if (!astlpc->lpc_map)
389 astlpc->priv_hdr = __mctp_alloc(sizeof(*astlpc->priv_hdr));
390
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530391 return astlpc;
392}
393
Jeremy Kerrb214c642019-11-27 11:34:00 +0800394#ifdef MCTP_HAVE_FILEIO
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530395static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc)
Jeremy Kerr672c8852019-03-01 12:18:07 +0800396{
397 struct aspeed_lpc_ctrl_mapping map = {
398 .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY,
399 .window_id = 0, /* There's only one */
400 .flags = 0,
401 .addr = 0,
402 .offset = 0,
403 .size = 0
404 };
405 int fd, rc;
406
407 fd = open(lpc_path, O_RDWR | O_SYNC);
408 if (fd < 0) {
409 mctp_prwarn("LPC open (%s) failed", lpc_path);
410 return -1;
411 }
412
413 rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map);
414 if (rc) {
415 mctp_prwarn("LPC GET_SIZE failed");
416 close(fd);
417 return -1;
418 }
419
420 astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE,
421 MAP_SHARED, fd, 0);
422 if (astlpc->lpc_map_base == MAP_FAILED) {
423 mctp_prwarn("LPC mmap failed");
424 rc = -1;
425 } else {
426 astlpc->lpc_map = astlpc->lpc_map_base +
427 map.size - LPC_WIN_SIZE;
428 }
429
430 close(fd);
431
432 return rc;
433}
434
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530435static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc)
Jeremy Kerr672c8852019-03-01 12:18:07 +0800436{
437 astlpc->kcs_fd = open(kcs_path, O_RDWR);
438 if (astlpc->kcs_fd < 0)
439 return -1;
440
441 return 0;
442}
443
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530444static int __mctp_astlpc_fileio_kcs_read(void *arg,
445 enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val)
446{
447 struct mctp_binding_astlpc *astlpc = arg;
448 off_t offset = reg;
449 int rc;
450
451 rc = pread(astlpc->kcs_fd, val, 1, offset);
452
453 return rc == 1 ? 0 : -1;
454}
455
456static int __mctp_astlpc_fileio_kcs_write(void *arg,
457 enum mctp_binding_astlpc_kcs_reg reg, uint8_t val)
458{
459 struct mctp_binding_astlpc *astlpc = arg;
460 off_t offset = reg;
461 int rc;
462
463 rc = pwrite(astlpc->kcs_fd, &val, 1, offset);
464
465 return rc == 1 ? 0 : -1;
466}
467
468int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc)
469{
470 return astlpc->kcs_fd;
471}
472
473struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void)
Jeremy Kerr672c8852019-03-01 12:18:07 +0800474{
475 struct mctp_binding_astlpc *astlpc;
476 int rc;
477
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530478 astlpc = __mctp_astlpc_init();
479 if (!astlpc)
480 return NULL;
Jeremy Kerr672c8852019-03-01 12:18:07 +0800481
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530482 /* Set internal operations for kcs. We use direct accesses to the lpc
483 * map area */
484 astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read;
485 astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write;
486 astlpc->ops_data = astlpc;
487
488 rc = mctp_astlpc_init_fileio_lpc(astlpc);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800489 if (rc) {
490 free(astlpc);
491 return NULL;
492 }
493
Jeremy Kerrbc53d352019-08-28 14:26:14 +0530494 rc = mctp_astlpc_init_fileio_kcs(astlpc);
Jeremy Kerr672c8852019-03-01 12:18:07 +0800495 if (rc) {
496 free(astlpc);
497 return NULL;
498 }
499
Jeremy Kerr672c8852019-03-01 12:18:07 +0800500 return astlpc;
501}
Jeremy Kerr92a10a62019-08-28 16:55:54 +0530502#else
503struct mctp_binding_astlpc * __attribute__((const))
504 mctp_astlpc_init_fileio(void)
505{
Andrew Jeffery59c6a5c2020-01-17 15:52:51 +1030506 warnx("Missing support for file IO");
Jeremy Kerr92a10a62019-08-28 16:55:54 +0530507 return NULL;
508}
Jeremy Kerr672c8852019-03-01 12:18:07 +0800509
Jeremy Kerr92a10a62019-08-28 16:55:54 +0530510int __attribute__((const)) mctp_astlpc_get_fd(
511 struct mctp_binding_astlpc *astlpc __attribute__((unused)))
512{
Andrew Jeffery59c6a5c2020-01-17 15:52:51 +1030513 warnx("Missing support for file IO");
Jeremy Kerr92a10a62019-08-28 16:55:54 +0530514 return -1;
515}
516#endif