1*672c8852SJeremy Kerr /* SPDX-License-Identifier: Apache-2.0 */ 2*672c8852SJeremy Kerr 3*672c8852SJeremy Kerr #include <assert.h> 4*672c8852SJeremy Kerr #include <fcntl.h> 5*672c8852SJeremy Kerr #include <stdbool.h> 6*672c8852SJeremy Kerr #include <stdlib.h> 7*672c8852SJeremy Kerr #include <string.h> 8*672c8852SJeremy Kerr #include <unistd.h> 9*672c8852SJeremy Kerr 10*672c8852SJeremy Kerr #include <sys/ioctl.h> 11*672c8852SJeremy Kerr #include <sys/mman.h> 12*672c8852SJeremy Kerr 13*672c8852SJeremy Kerr #include <linux/aspeed-lpc-ctrl.h> 14*672c8852SJeremy Kerr 15*672c8852SJeremy Kerr #define pr_fmt(x) "astlpc: " x 16*672c8852SJeremy Kerr 17*672c8852SJeremy Kerr #include "libmctp.h" 18*672c8852SJeremy Kerr #include "libmctp-alloc.h" 19*672c8852SJeremy Kerr #include "libmctp-log.h" 20*672c8852SJeremy Kerr #include "libmctp-astlpc.h" 21*672c8852SJeremy Kerr 22*672c8852SJeremy Kerr struct mctp_binding_astlpc { 23*672c8852SJeremy Kerr struct mctp_binding binding; 24*672c8852SJeremy Kerr void *lpc_map_base; 25*672c8852SJeremy Kerr union { 26*672c8852SJeremy Kerr void *lpc_map; 27*672c8852SJeremy Kerr struct mctp_lpcmap_hdr *lpc_hdr; 28*672c8852SJeremy Kerr }; 29*672c8852SJeremy Kerr int kcs_fd; 30*672c8852SJeremy Kerr uint8_t kcs_status; 31*672c8852SJeremy Kerr 32*672c8852SJeremy Kerr bool running; 33*672c8852SJeremy Kerr 34*672c8852SJeremy Kerr /* temporary transmit buffer */ 35*672c8852SJeremy Kerr uint8_t txbuf[256]; 36*672c8852SJeremy Kerr }; 37*672c8852SJeremy Kerr 38*672c8852SJeremy Kerr #ifndef container_of 39*672c8852SJeremy Kerr #define container_of(ptr, type, member) \ 40*672c8852SJeremy Kerr (type *)((char *)(ptr) - (char *)&((type *)0)->member) 41*672c8852SJeremy Kerr #endif 42*672c8852SJeremy Kerr 43*672c8852SJeremy Kerr #define binding_to_astlpc(b) \ 44*672c8852SJeremy Kerr container_of(b, struct mctp_binding_astlpc, binding) 45*672c8852SJeremy Kerr 46*672c8852SJeremy Kerr #define MCTP_MAGIC 0x4d435450 47*672c8852SJeremy Kerr #define BMC_VER_MIN 1 48*672c8852SJeremy Kerr #define BMC_VER_CUR 1 49*672c8852SJeremy Kerr 50*672c8852SJeremy Kerr struct mctp_lpcmap_hdr { 51*672c8852SJeremy Kerr uint32_t magic; 52*672c8852SJeremy Kerr 53*672c8852SJeremy Kerr uint16_t bmc_ver_min; 54*672c8852SJeremy Kerr uint16_t bmc_ver_cur; 55*672c8852SJeremy Kerr uint16_t host_ver_min; 56*672c8852SJeremy Kerr uint16_t host_ver_cur; 57*672c8852SJeremy Kerr uint16_t negotiated_ver; 58*672c8852SJeremy Kerr uint16_t pad0; 59*672c8852SJeremy Kerr 60*672c8852SJeremy Kerr uint32_t rx_offset; 61*672c8852SJeremy Kerr uint32_t rx_size; 62*672c8852SJeremy Kerr uint32_t tx_offset; 63*672c8852SJeremy Kerr uint32_t tx_size; 64*672c8852SJeremy Kerr } __attribute__((packed)); 65*672c8852SJeremy Kerr 66*672c8852SJeremy Kerr /* layout of TX/RX areas */ 67*672c8852SJeremy Kerr static const uint32_t rx_offset = 0x100; 68*672c8852SJeremy Kerr static const uint32_t rx_size = 0x100; 69*672c8852SJeremy Kerr static const uint32_t tx_offset = 0x200; 70*672c8852SJeremy Kerr static const uint32_t tx_size = 0x100; 71*672c8852SJeremy Kerr 72*672c8852SJeremy Kerr /* kernel interface */ 73*672c8852SJeremy Kerr static const char *kcs_path = "/dev/mctp0"; 74*672c8852SJeremy Kerr static const char *lpc_path = "/dev/aspeed-lpc-ctrl"; 75*672c8852SJeremy Kerr 76*672c8852SJeremy Kerr #define LPC_WIN_SIZE (1 * 1024 * 1024) 77*672c8852SJeremy Kerr 78*672c8852SJeremy Kerr enum { 79*672c8852SJeremy Kerr KCS_REG_DATA = 0, 80*672c8852SJeremy Kerr KCS_REG_STATUS = 1, 81*672c8852SJeremy Kerr }; 82*672c8852SJeremy Kerr 83*672c8852SJeremy Kerr #define KCS_STATUS_BMC_READY 0x80 84*672c8852SJeremy Kerr #define KCS_STATUS_CHANNEL_ACTIVE 0x40 85*672c8852SJeremy Kerr #define KCS_STATUS_IBF 0x02 86*672c8852SJeremy Kerr #define KCS_STATUS_OBF 0x01 87*672c8852SJeremy Kerr 88*672c8852SJeremy Kerr static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc, 89*672c8852SJeremy Kerr uint8_t status) 90*672c8852SJeremy Kerr { 91*672c8852SJeremy Kerr int rc; 92*672c8852SJeremy Kerr 93*672c8852SJeremy Kerr rc = pwrite(astlpc->kcs_fd, &status, 1, KCS_REG_STATUS); 94*672c8852SJeremy Kerr if (rc != 1) { 95*672c8852SJeremy Kerr mctp_prwarn("KCS status write failed"); 96*672c8852SJeremy Kerr return -1; 97*672c8852SJeremy Kerr } 98*672c8852SJeremy Kerr return 0; 99*672c8852SJeremy Kerr } 100*672c8852SJeremy Kerr 101*672c8852SJeremy Kerr static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc, 102*672c8852SJeremy Kerr uint8_t data) 103*672c8852SJeremy Kerr { 104*672c8852SJeremy Kerr uint8_t status; 105*672c8852SJeremy Kerr int rc; 106*672c8852SJeremy Kerr 107*672c8852SJeremy Kerr for (;;) { 108*672c8852SJeremy Kerr rc = pread(astlpc->kcs_fd, &status, 1, KCS_REG_STATUS); 109*672c8852SJeremy Kerr if (rc != 1) { 110*672c8852SJeremy Kerr mctp_prwarn("KCE status read failed"); 111*672c8852SJeremy Kerr return -1; 112*672c8852SJeremy Kerr } 113*672c8852SJeremy Kerr if (!(status & KCS_STATUS_OBF)) 114*672c8852SJeremy Kerr break; 115*672c8852SJeremy Kerr /* todo: timeout */ 116*672c8852SJeremy Kerr } 117*672c8852SJeremy Kerr 118*672c8852SJeremy Kerr rc = pwrite(astlpc->kcs_fd, &data, 1, KCS_REG_DATA); 119*672c8852SJeremy Kerr if (rc != 1) { 120*672c8852SJeremy Kerr mctp_prwarn("KCS data write failed"); 121*672c8852SJeremy Kerr return -1; 122*672c8852SJeremy Kerr } 123*672c8852SJeremy Kerr 124*672c8852SJeremy Kerr return 0; 125*672c8852SJeremy Kerr } 126*672c8852SJeremy Kerr 127*672c8852SJeremy Kerr static int mctp_binding_astlpc_tx(struct mctp_binding *b, 128*672c8852SJeremy Kerr struct mctp_pktbuf *pkt) 129*672c8852SJeremy Kerr { 130*672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b); 131*672c8852SJeremy Kerr uint32_t len; 132*672c8852SJeremy Kerr 133*672c8852SJeremy Kerr len = mctp_pktbuf_size(pkt); 134*672c8852SJeremy Kerr if (len > rx_size - 4) { 135*672c8852SJeremy Kerr mctp_prwarn("invalid TX len 0x%x", len); 136*672c8852SJeremy Kerr return -1; 137*672c8852SJeremy Kerr } 138*672c8852SJeremy Kerr 139*672c8852SJeremy Kerr *(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len); 140*672c8852SJeremy Kerr 141*672c8852SJeremy Kerr memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt), len); 142*672c8852SJeremy Kerr 143*672c8852SJeremy Kerr mctp_binding_set_tx_enabled(b, false); 144*672c8852SJeremy Kerr 145*672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x1); 146*672c8852SJeremy Kerr return 0; 147*672c8852SJeremy Kerr } 148*672c8852SJeremy Kerr 149*672c8852SJeremy Kerr static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc) 150*672c8852SJeremy Kerr { 151*672c8852SJeremy Kerr /* todo: actual version negotiation */ 152*672c8852SJeremy Kerr astlpc->lpc_hdr->negotiated_ver = htobe16(1); 153*672c8852SJeremy Kerr mctp_astlpc_kcs_set_status(astlpc, 154*672c8852SJeremy Kerr KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE | 155*672c8852SJeremy Kerr KCS_STATUS_OBF); 156*672c8852SJeremy Kerr 157*672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 158*672c8852SJeremy Kerr } 159*672c8852SJeremy Kerr 160*672c8852SJeremy Kerr static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc) 161*672c8852SJeremy Kerr { 162*672c8852SJeremy Kerr struct mctp_pktbuf *pkt; 163*672c8852SJeremy Kerr uint32_t len; 164*672c8852SJeremy Kerr 165*672c8852SJeremy Kerr len = htobe32(*(uint32_t *)(astlpc->lpc_map + tx_offset)); 166*672c8852SJeremy Kerr if (len > tx_size - 4) { 167*672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 168*672c8852SJeremy Kerr return; 169*672c8852SJeremy Kerr } 170*672c8852SJeremy Kerr 171*672c8852SJeremy Kerr if (len > MCTP_MTU + sizeof(struct mctp_hdr)) { 172*672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 173*672c8852SJeremy Kerr return; 174*672c8852SJeremy Kerr } 175*672c8852SJeremy Kerr 176*672c8852SJeremy Kerr pkt = mctp_pktbuf_alloc(len); 177*672c8852SJeremy Kerr if (!pkt) 178*672c8852SJeremy Kerr goto out_complete; 179*672c8852SJeremy Kerr 180*672c8852SJeremy Kerr memcpy(mctp_pktbuf_hdr(pkt), astlpc->lpc_map + tx_offset + 4, len); 181*672c8852SJeremy Kerr 182*672c8852SJeremy Kerr mctp_bus_rx(&astlpc->binding, pkt); 183*672c8852SJeremy Kerr 184*672c8852SJeremy Kerr out_complete: 185*672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x2); 186*672c8852SJeremy Kerr } 187*672c8852SJeremy Kerr 188*672c8852SJeremy Kerr static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc) 189*672c8852SJeremy Kerr { 190*672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 191*672c8852SJeremy Kerr } 192*672c8852SJeremy Kerr 193*672c8852SJeremy Kerr int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc) 194*672c8852SJeremy Kerr { 195*672c8852SJeremy Kerr uint8_t kcs_regs[2], data; 196*672c8852SJeremy Kerr int rc; 197*672c8852SJeremy Kerr 198*672c8852SJeremy Kerr rc = pread(astlpc->kcs_fd, kcs_regs, 2, 0); 199*672c8852SJeremy Kerr if (rc < 0) { 200*672c8852SJeremy Kerr mctp_prwarn("KCS read error"); 201*672c8852SJeremy Kerr return -1; 202*672c8852SJeremy Kerr } else if (rc != 2) { 203*672c8852SJeremy Kerr mctp_prwarn("KCS short read (%d)", rc); 204*672c8852SJeremy Kerr return -1; 205*672c8852SJeremy Kerr } 206*672c8852SJeremy Kerr 207*672c8852SJeremy Kerr if (!(kcs_regs[KCS_REG_STATUS] & KCS_STATUS_IBF)) 208*672c8852SJeremy Kerr return 0; 209*672c8852SJeremy Kerr 210*672c8852SJeremy Kerr data = kcs_regs[KCS_REG_DATA]; 211*672c8852SJeremy Kerr switch (data) { 212*672c8852SJeremy Kerr case 0x0: 213*672c8852SJeremy Kerr mctp_astlpc_init_channel(astlpc); 214*672c8852SJeremy Kerr break; 215*672c8852SJeremy Kerr case 0x1: 216*672c8852SJeremy Kerr mctp_astlpc_rx_start(astlpc); 217*672c8852SJeremy Kerr break; 218*672c8852SJeremy Kerr case 0x2: 219*672c8852SJeremy Kerr mctp_astlpc_tx_complete(astlpc); 220*672c8852SJeremy Kerr break; 221*672c8852SJeremy Kerr default: 222*672c8852SJeremy Kerr mctp_prwarn("unknown message 0x%x", data); 223*672c8852SJeremy Kerr } 224*672c8852SJeremy Kerr return 0; 225*672c8852SJeremy Kerr } 226*672c8852SJeremy Kerr 227*672c8852SJeremy Kerr int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc) 228*672c8852SJeremy Kerr { 229*672c8852SJeremy Kerr return astlpc->kcs_fd; 230*672c8852SJeremy Kerr } 231*672c8852SJeremy Kerr 232*672c8852SJeremy Kerr 233*672c8852SJeremy Kerr void mctp_astlpc_register_bus(struct mctp_binding_astlpc *astlpc, 234*672c8852SJeremy Kerr struct mctp *mctp, mctp_eid_t eid) 235*672c8852SJeremy Kerr { 236*672c8852SJeremy Kerr mctp_register_bus(mctp, &astlpc->binding, eid); 237*672c8852SJeremy Kerr } 238*672c8852SJeremy Kerr 239*672c8852SJeremy Kerr static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc) 240*672c8852SJeremy Kerr { 241*672c8852SJeremy Kerr uint8_t status; 242*672c8852SJeremy Kerr int rc; 243*672c8852SJeremy Kerr 244*672c8852SJeremy Kerr astlpc->lpc_hdr->magic = htobe32(MCTP_MAGIC); 245*672c8852SJeremy Kerr astlpc->lpc_hdr->bmc_ver_min = htobe16(BMC_VER_MIN); 246*672c8852SJeremy Kerr astlpc->lpc_hdr->bmc_ver_cur = htobe16(BMC_VER_CUR); 247*672c8852SJeremy Kerr 248*672c8852SJeremy Kerr astlpc->lpc_hdr->rx_offset = htobe32(rx_offset); 249*672c8852SJeremy Kerr astlpc->lpc_hdr->rx_size = htobe32(rx_size); 250*672c8852SJeremy Kerr astlpc->lpc_hdr->tx_offset = htobe32(tx_offset); 251*672c8852SJeremy Kerr astlpc->lpc_hdr->tx_size = htobe32(tx_size); 252*672c8852SJeremy Kerr 253*672c8852SJeremy Kerr /* set status indicating that the BMC is now active */ 254*672c8852SJeremy Kerr status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF; 255*672c8852SJeremy Kerr rc = pwrite(astlpc->kcs_fd, &status, 1, KCS_REG_STATUS); 256*672c8852SJeremy Kerr if (rc != 1) { 257*672c8852SJeremy Kerr mctp_prwarn("KCS write failed"); 258*672c8852SJeremy Kerr rc = -1; 259*672c8852SJeremy Kerr } else { 260*672c8852SJeremy Kerr rc = 0; 261*672c8852SJeremy Kerr } 262*672c8852SJeremy Kerr 263*672c8852SJeremy Kerr return rc; 264*672c8852SJeremy Kerr } 265*672c8852SJeremy Kerr 266*672c8852SJeremy Kerr static int mctp_astlpc_init_lpc(struct mctp_binding_astlpc *astlpc) 267*672c8852SJeremy Kerr { 268*672c8852SJeremy Kerr struct aspeed_lpc_ctrl_mapping map = { 269*672c8852SJeremy Kerr .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY, 270*672c8852SJeremy Kerr .window_id = 0, /* There's only one */ 271*672c8852SJeremy Kerr .flags = 0, 272*672c8852SJeremy Kerr .addr = 0, 273*672c8852SJeremy Kerr .offset = 0, 274*672c8852SJeremy Kerr .size = 0 275*672c8852SJeremy Kerr }; 276*672c8852SJeremy Kerr int fd, rc; 277*672c8852SJeremy Kerr 278*672c8852SJeremy Kerr fd = open(lpc_path, O_RDWR | O_SYNC); 279*672c8852SJeremy Kerr if (fd < 0) { 280*672c8852SJeremy Kerr mctp_prwarn("LPC open (%s) failed", lpc_path); 281*672c8852SJeremy Kerr return -1; 282*672c8852SJeremy Kerr } 283*672c8852SJeremy Kerr 284*672c8852SJeremy Kerr rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map); 285*672c8852SJeremy Kerr if (rc) { 286*672c8852SJeremy Kerr mctp_prwarn("LPC GET_SIZE failed"); 287*672c8852SJeremy Kerr close(fd); 288*672c8852SJeremy Kerr return -1; 289*672c8852SJeremy Kerr } 290*672c8852SJeremy Kerr 291*672c8852SJeremy Kerr astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE, 292*672c8852SJeremy Kerr MAP_SHARED, fd, 0); 293*672c8852SJeremy Kerr if (astlpc->lpc_map_base == MAP_FAILED) { 294*672c8852SJeremy Kerr mctp_prwarn("LPC mmap failed"); 295*672c8852SJeremy Kerr rc = -1; 296*672c8852SJeremy Kerr } else { 297*672c8852SJeremy Kerr astlpc->lpc_map = astlpc->lpc_map_base + 298*672c8852SJeremy Kerr map.size - LPC_WIN_SIZE; 299*672c8852SJeremy Kerr } 300*672c8852SJeremy Kerr 301*672c8852SJeremy Kerr close(fd); 302*672c8852SJeremy Kerr 303*672c8852SJeremy Kerr return rc; 304*672c8852SJeremy Kerr } 305*672c8852SJeremy Kerr 306*672c8852SJeremy Kerr static int mctp_astlpc_init_kcs(struct mctp_binding_astlpc *astlpc) 307*672c8852SJeremy Kerr { 308*672c8852SJeremy Kerr astlpc->kcs_fd = open(kcs_path, O_RDWR); 309*672c8852SJeremy Kerr if (astlpc->kcs_fd < 0) 310*672c8852SJeremy Kerr return -1; 311*672c8852SJeremy Kerr 312*672c8852SJeremy Kerr return 0; 313*672c8852SJeremy Kerr } 314*672c8852SJeremy Kerr 315*672c8852SJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init(void) 316*672c8852SJeremy Kerr { 317*672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc; 318*672c8852SJeremy Kerr int rc; 319*672c8852SJeremy Kerr 320*672c8852SJeremy Kerr astlpc = __mctp_alloc(sizeof(*astlpc)); 321*672c8852SJeremy Kerr memset(astlpc, 0, sizeof(*astlpc)); 322*672c8852SJeremy Kerr astlpc->binding.name = "astlpc"; 323*672c8852SJeremy Kerr astlpc->binding.version = 1; 324*672c8852SJeremy Kerr astlpc->binding.tx = mctp_binding_astlpc_tx; 325*672c8852SJeremy Kerr 326*672c8852SJeremy Kerr rc = mctp_astlpc_init_lpc(astlpc); 327*672c8852SJeremy Kerr if (rc) { 328*672c8852SJeremy Kerr free(astlpc); 329*672c8852SJeremy Kerr return NULL; 330*672c8852SJeremy Kerr } 331*672c8852SJeremy Kerr 332*672c8852SJeremy Kerr rc = mctp_astlpc_init_kcs(astlpc); 333*672c8852SJeremy Kerr if (rc) { 334*672c8852SJeremy Kerr free(astlpc); 335*672c8852SJeremy Kerr return NULL; 336*672c8852SJeremy Kerr } 337*672c8852SJeremy Kerr 338*672c8852SJeremy Kerr rc = mctp_astlpc_init_bmc(astlpc); 339*672c8852SJeremy Kerr if (rc) { 340*672c8852SJeremy Kerr free(astlpc); 341*672c8852SJeremy Kerr return NULL; 342*672c8852SJeremy Kerr } 343*672c8852SJeremy Kerr 344*672c8852SJeremy Kerr return astlpc; 345*672c8852SJeremy Kerr } 346*672c8852SJeremy Kerr 347