13d36ee2eSJeremy Kerr /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2672c8852SJeremy Kerr 33286f176SAndrew Jeffery #if HAVE_CONFIG_H 43286f176SAndrew Jeffery #include "config.h" 53286f176SAndrew Jeffery #endif 63286f176SAndrew Jeffery 73286f176SAndrew Jeffery #if HAVE_ENDIAN_H 892a10a6bSJeremy Kerr #include <endian.h> 93286f176SAndrew Jeffery #endif 103286f176SAndrew Jeffery 113286f176SAndrew Jeffery #include <assert.h> 1259c6a5c9SAndrew Jeffery #include <err.h> 13edfe383fSAndrew Jeffery #include <inttypes.h> 14672c8852SJeremy Kerr #include <stdbool.h> 15672c8852SJeremy Kerr #include <stdlib.h> 16672c8852SJeremy Kerr #include <string.h> 17672c8852SJeremy Kerr 18672c8852SJeremy Kerr #define pr_fmt(x) "astlpc: " x 19672c8852SJeremy Kerr 20672c8852SJeremy Kerr #include "libmctp.h" 21672c8852SJeremy Kerr #include "libmctp-alloc.h" 22672c8852SJeremy Kerr #include "libmctp-log.h" 23672c8852SJeremy Kerr #include "libmctp-astlpc.h" 24ff25d7ebSPrzemyslaw Czarnowski #include "container_of.h" 25672c8852SJeremy Kerr 26b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO 2792a10a6bSJeremy Kerr 28c6f676d1SJeremy Kerr #include <unistd.h> 2992a10a6bSJeremy Kerr #include <fcntl.h> 3092a10a6bSJeremy Kerr #include <sys/ioctl.h> 3192a10a6bSJeremy Kerr #include <sys/mman.h> 3292a10a6bSJeremy Kerr #include <linux/aspeed-lpc-ctrl.h> 3392a10a6bSJeremy Kerr 3492a10a6bSJeremy Kerr /* kernel interface */ 3592a10a6bSJeremy Kerr static const char *kcs_path = "/dev/mctp0"; 3692a10a6bSJeremy Kerr static const char *lpc_path = "/dev/aspeed-lpc-ctrl"; 3792a10a6bSJeremy Kerr 3892a10a6bSJeremy Kerr #endif 3992a10a6bSJeremy Kerr 40672c8852SJeremy Kerr struct mctp_binding_astlpc { 41672c8852SJeremy Kerr struct mctp_binding binding; 42bc53d35aSJeremy Kerr 43672c8852SJeremy Kerr union { 44672c8852SJeremy Kerr void *lpc_map; 45672c8852SJeremy Kerr struct mctp_lpcmap_hdr *lpc_hdr; 46672c8852SJeremy Kerr }; 47bc53d35aSJeremy Kerr 48bc53d35aSJeremy Kerr /* direct ops data */ 49bc53d35aSJeremy Kerr struct mctp_binding_astlpc_ops ops; 50bc53d35aSJeremy Kerr void *ops_data; 51bc53d35aSJeremy Kerr struct mctp_lpcmap_hdr *priv_hdr; 52bc53d35aSJeremy Kerr 53bc53d35aSJeremy Kerr /* fileio ops data */ 54bc53d35aSJeremy Kerr void *lpc_map_base; 55672c8852SJeremy Kerr int kcs_fd; 56672c8852SJeremy Kerr uint8_t kcs_status; 57672c8852SJeremy Kerr 58672c8852SJeremy Kerr bool running; 59672c8852SJeremy Kerr }; 60672c8852SJeremy Kerr 61672c8852SJeremy Kerr #define binding_to_astlpc(b) \ 62672c8852SJeremy Kerr container_of(b, struct mctp_binding_astlpc, binding) 63672c8852SJeremy Kerr 64672c8852SJeremy Kerr #define MCTP_MAGIC 0x4d435450 65672c8852SJeremy Kerr #define BMC_VER_MIN 1 66672c8852SJeremy Kerr #define BMC_VER_CUR 1 67672c8852SJeremy Kerr 68672c8852SJeremy Kerr struct mctp_lpcmap_hdr { 69672c8852SJeremy Kerr uint32_t magic; 70672c8852SJeremy Kerr 71672c8852SJeremy Kerr uint16_t bmc_ver_min; 72672c8852SJeremy Kerr uint16_t bmc_ver_cur; 73672c8852SJeremy Kerr uint16_t host_ver_min; 74672c8852SJeremy Kerr uint16_t host_ver_cur; 75672c8852SJeremy Kerr uint16_t negotiated_ver; 76672c8852SJeremy Kerr uint16_t pad0; 77672c8852SJeremy Kerr 78672c8852SJeremy Kerr uint32_t rx_offset; 79672c8852SJeremy Kerr uint32_t rx_size; 80672c8852SJeremy Kerr uint32_t tx_offset; 81672c8852SJeremy Kerr uint32_t tx_size; 82672c8852SJeremy Kerr } __attribute__((packed)); 83672c8852SJeremy Kerr 84672c8852SJeremy Kerr /* layout of TX/RX areas */ 85672c8852SJeremy Kerr static const uint32_t rx_offset = 0x100; 86672c8852SJeremy Kerr static const uint32_t rx_size = 0x100; 87672c8852SJeremy Kerr static const uint32_t tx_offset = 0x200; 88672c8852SJeremy Kerr static const uint32_t tx_size = 0x100; 89672c8852SJeremy Kerr 90672c8852SJeremy Kerr #define LPC_WIN_SIZE (1 * 1024 * 1024) 91672c8852SJeremy Kerr 92672c8852SJeremy Kerr enum { 93672c8852SJeremy Kerr KCS_REG_DATA = 0, 94672c8852SJeremy Kerr KCS_REG_STATUS = 1, 95672c8852SJeremy Kerr }; 96672c8852SJeremy Kerr 97672c8852SJeremy Kerr #define KCS_STATUS_BMC_READY 0x80 98672c8852SJeremy Kerr #define KCS_STATUS_CHANNEL_ACTIVE 0x40 99672c8852SJeremy Kerr #define KCS_STATUS_IBF 0x02 100672c8852SJeremy Kerr #define KCS_STATUS_OBF 0x01 101672c8852SJeremy Kerr 102bc53d35aSJeremy Kerr static bool lpc_direct(struct mctp_binding_astlpc *astlpc) 103bc53d35aSJeremy Kerr { 104bc53d35aSJeremy Kerr return astlpc->lpc_map != NULL; 105bc53d35aSJeremy Kerr } 106bc53d35aSJeremy Kerr 107672c8852SJeremy Kerr static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc, 108672c8852SJeremy Kerr uint8_t status) 109672c8852SJeremy Kerr { 110bc53d35aSJeremy Kerr uint8_t data; 111672c8852SJeremy Kerr int rc; 112672c8852SJeremy Kerr 1131a4b55acSJeremy Kerr /* Since we're setting the status register, we want the other endpoint 1141a4b55acSJeremy Kerr * to be interrupted. However, some hardware may only raise a host-side 1151a4b55acSJeremy Kerr * interrupt on an ODR event. 1161a4b55acSJeremy Kerr * So, write a dummy value of 0xff to ODR, which will ensure that an 1171a4b55acSJeremy Kerr * interrupt is triggered, and can be ignored by the host. 1181a4b55acSJeremy Kerr */ 119bc53d35aSJeremy Kerr data = 0xff; 120bc53d35aSJeremy Kerr status |= KCS_STATUS_OBF; 1211a4b55acSJeremy Kerr 122c84fd56dSAndrew Jeffery rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 123c84fd56dSAndrew Jeffery status); 124c84fd56dSAndrew Jeffery if (rc) { 125c84fd56dSAndrew Jeffery mctp_prwarn("KCS status write failed"); 126c84fd56dSAndrew Jeffery return -1; 127c84fd56dSAndrew Jeffery } 128c84fd56dSAndrew Jeffery 129bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 130bc53d35aSJeremy Kerr data); 131bc53d35aSJeremy Kerr if (rc) { 132bc53d35aSJeremy Kerr mctp_prwarn("KCS dummy data write failed"); 133bc53d35aSJeremy Kerr return -1; 134bc53d35aSJeremy Kerr } 135bc53d35aSJeremy Kerr 136672c8852SJeremy Kerr return 0; 137672c8852SJeremy Kerr } 138672c8852SJeremy Kerr 139672c8852SJeremy Kerr static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc, 140672c8852SJeremy Kerr uint8_t data) 141672c8852SJeremy Kerr { 142672c8852SJeremy Kerr uint8_t status; 143672c8852SJeremy Kerr int rc; 144672c8852SJeremy Kerr 145672c8852SJeremy Kerr for (;;) { 146bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, 147bc53d35aSJeremy Kerr MCTP_ASTLPC_KCS_REG_STATUS, &status); 1481b27fe87SAndrew Jeffery if (rc) { 149182204e3SAndrew Jeffery mctp_prwarn("KCS status read failed"); 150672c8852SJeremy Kerr return -1; 151672c8852SJeremy Kerr } 152672c8852SJeremy Kerr if (!(status & KCS_STATUS_OBF)) 153672c8852SJeremy Kerr break; 154672c8852SJeremy Kerr /* todo: timeout */ 155672c8852SJeremy Kerr } 156672c8852SJeremy Kerr 157bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 158bc53d35aSJeremy Kerr data); 159bc53d35aSJeremy Kerr if (rc) { 160672c8852SJeremy Kerr mctp_prwarn("KCS data write failed"); 161672c8852SJeremy Kerr return -1; 162672c8852SJeremy Kerr } 163672c8852SJeremy Kerr 164672c8852SJeremy Kerr return 0; 165672c8852SJeremy Kerr } 166672c8852SJeremy Kerr 167672c8852SJeremy Kerr static int mctp_binding_astlpc_tx(struct mctp_binding *b, 168672c8852SJeremy Kerr struct mctp_pktbuf *pkt) 169672c8852SJeremy Kerr { 170672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b); 171672c8852SJeremy Kerr uint32_t len; 172edfe383fSAndrew Jeffery struct mctp_hdr *hdr; 173672c8852SJeremy Kerr 174edfe383fSAndrew Jeffery hdr = mctp_pktbuf_hdr(pkt); 175672c8852SJeremy Kerr len = mctp_pktbuf_size(pkt); 176edfe383fSAndrew Jeffery 177edfe383fSAndrew Jeffery mctp_prdebug("%s: Transmitting %"PRIu32"-byte packet (%hhu, %hhu, 0x%hhx)", 178edfe383fSAndrew Jeffery __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag); 179edfe383fSAndrew Jeffery 180672c8852SJeremy Kerr if (len > rx_size - 4) { 181672c8852SJeremy Kerr mctp_prwarn("invalid TX len 0x%x", len); 182672c8852SJeremy Kerr return -1; 183672c8852SJeremy Kerr } 184672c8852SJeremy Kerr 185bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 186672c8852SJeremy Kerr *(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len); 187bc53d35aSJeremy Kerr memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt), 188bc53d35aSJeremy Kerr len); 189bc53d35aSJeremy Kerr } else { 190bc53d35aSJeremy Kerr uint32_t tmp = htobe32(len); 191bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, &tmp, rx_offset, 192bc53d35aSJeremy Kerr sizeof(tmp)); 193bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 194bc53d35aSJeremy Kerr rx_offset + 4, len); 195bc53d35aSJeremy Kerr } 196672c8852SJeremy Kerr 197672c8852SJeremy Kerr mctp_binding_set_tx_enabled(b, false); 198672c8852SJeremy Kerr 199672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x1); 200672c8852SJeremy Kerr return 0; 201672c8852SJeremy Kerr } 202672c8852SJeremy Kerr 203672c8852SJeremy Kerr static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc) 204672c8852SJeremy Kerr { 205672c8852SJeremy Kerr /* todo: actual version negotiation */ 206bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 207672c8852SJeremy Kerr astlpc->lpc_hdr->negotiated_ver = htobe16(1); 208bc53d35aSJeremy Kerr } else { 209bc53d35aSJeremy Kerr uint16_t ver = htobe16(1); 210bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, &ver, 211bc53d35aSJeremy Kerr offsetof(struct mctp_lpcmap_hdr, 212bc53d35aSJeremy Kerr negotiated_ver), 213bc53d35aSJeremy Kerr sizeof(ver)); 214bc53d35aSJeremy Kerr } 215672c8852SJeremy Kerr mctp_astlpc_kcs_set_status(astlpc, 216672c8852SJeremy Kerr KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE | 217672c8852SJeremy Kerr KCS_STATUS_OBF); 218672c8852SJeremy Kerr 219672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 220672c8852SJeremy Kerr } 221672c8852SJeremy Kerr 222672c8852SJeremy Kerr static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc) 223672c8852SJeremy Kerr { 224672c8852SJeremy Kerr struct mctp_pktbuf *pkt; 225672c8852SJeremy Kerr uint32_t len; 226672c8852SJeremy Kerr 227bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 228bc53d35aSJeremy Kerr len = *(uint32_t *)(astlpc->lpc_map + tx_offset); 229bc53d35aSJeremy Kerr } else { 230bc53d35aSJeremy Kerr astlpc->ops.lpc_read(astlpc->ops_data, &len, 231bc53d35aSJeremy Kerr tx_offset, sizeof(len)); 232bc53d35aSJeremy Kerr } 233bc53d35aSJeremy Kerr len = be32toh(len); 234bc53d35aSJeremy Kerr 235672c8852SJeremy Kerr if (len > tx_size - 4) { 236672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 237672c8852SJeremy Kerr return; 238672c8852SJeremy Kerr } 239672c8852SJeremy Kerr 240*b93b6112SAndrew Jeffery assert(astlpc->binding.pkt_size >= 0); 241*b93b6112SAndrew Jeffery if (len > (uint32_t)astlpc->binding.pkt_size) { 242672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 243672c8852SJeremy Kerr return; 244672c8852SJeremy Kerr } 245672c8852SJeremy Kerr 246df15f7e9SJeremy Kerr pkt = mctp_pktbuf_alloc(&astlpc->binding, len); 247672c8852SJeremy Kerr if (!pkt) 248672c8852SJeremy Kerr goto out_complete; 249672c8852SJeremy Kerr 250bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 251bc53d35aSJeremy Kerr memcpy(mctp_pktbuf_hdr(pkt), 252bc53d35aSJeremy Kerr astlpc->lpc_map + tx_offset + 4, len); 253bc53d35aSJeremy Kerr } else { 254bc53d35aSJeremy Kerr astlpc->ops.lpc_read(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 255bc53d35aSJeremy Kerr tx_offset + 4, len); 256bc53d35aSJeremy Kerr } 257672c8852SJeremy Kerr 258672c8852SJeremy Kerr mctp_bus_rx(&astlpc->binding, pkt); 259672c8852SJeremy Kerr 260672c8852SJeremy Kerr out_complete: 261672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x2); 262672c8852SJeremy Kerr } 263672c8852SJeremy Kerr 264672c8852SJeremy Kerr static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc) 265672c8852SJeremy Kerr { 266672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 267672c8852SJeremy Kerr } 268672c8852SJeremy Kerr 269672c8852SJeremy Kerr int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc) 270672c8852SJeremy Kerr { 271bc53d35aSJeremy Kerr uint8_t status, data; 272672c8852SJeremy Kerr int rc; 273672c8852SJeremy Kerr 274bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 275bc53d35aSJeremy Kerr &status); 276bc53d35aSJeremy Kerr if (rc) { 277672c8852SJeremy Kerr mctp_prwarn("KCS read error"); 278672c8852SJeremy Kerr return -1; 279672c8852SJeremy Kerr } 280672c8852SJeremy Kerr 281edfe383fSAndrew Jeffery mctp_prdebug("%s: status: 0x%hhx", __func__, status); 282edfe383fSAndrew Jeffery 283bc53d35aSJeremy Kerr if (!(status & KCS_STATUS_IBF)) 284672c8852SJeremy Kerr return 0; 285672c8852SJeremy Kerr 286bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 287bc53d35aSJeremy Kerr &data); 288bc53d35aSJeremy Kerr if (rc) { 289bc53d35aSJeremy Kerr mctp_prwarn("KCS data read error"); 290bc53d35aSJeremy Kerr return -1; 291bc53d35aSJeremy Kerr } 292bc53d35aSJeremy Kerr 293edfe383fSAndrew Jeffery mctp_prdebug("%s: data: 0x%hhx", __func__, data); 294edfe383fSAndrew Jeffery 295672c8852SJeremy Kerr switch (data) { 296672c8852SJeremy Kerr case 0x0: 297672c8852SJeremy Kerr mctp_astlpc_init_channel(astlpc); 298672c8852SJeremy Kerr break; 299672c8852SJeremy Kerr case 0x1: 300672c8852SJeremy Kerr mctp_astlpc_rx_start(astlpc); 301672c8852SJeremy Kerr break; 302672c8852SJeremy Kerr case 0x2: 303672c8852SJeremy Kerr mctp_astlpc_tx_complete(astlpc); 304672c8852SJeremy Kerr break; 3051a4b55acSJeremy Kerr case 0xff: 3061a4b55acSJeremy Kerr /* reserved value for dummy data writes; do nothing */ 3071a4b55acSJeremy Kerr break; 308672c8852SJeremy Kerr default: 309672c8852SJeremy Kerr mctp_prwarn("unknown message 0x%x", data); 310672c8852SJeremy Kerr } 311672c8852SJeremy Kerr return 0; 312672c8852SJeremy Kerr } 313672c8852SJeremy Kerr 314672c8852SJeremy Kerr static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc) 315672c8852SJeremy Kerr { 316bc53d35aSJeremy Kerr struct mctp_lpcmap_hdr *hdr; 317672c8852SJeremy Kerr uint8_t status; 318672c8852SJeremy Kerr int rc; 319672c8852SJeremy Kerr 320bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) 321bc53d35aSJeremy Kerr hdr = astlpc->lpc_hdr; 322bc53d35aSJeremy Kerr else 323bc53d35aSJeremy Kerr hdr = astlpc->priv_hdr; 324672c8852SJeremy Kerr 325bc53d35aSJeremy Kerr hdr->magic = htobe32(MCTP_MAGIC); 326bc53d35aSJeremy Kerr hdr->bmc_ver_min = htobe16(BMC_VER_MIN); 327bc53d35aSJeremy Kerr hdr->bmc_ver_cur = htobe16(BMC_VER_CUR); 328bc53d35aSJeremy Kerr 329bc53d35aSJeremy Kerr hdr->rx_offset = htobe32(rx_offset); 330bc53d35aSJeremy Kerr hdr->rx_size = htobe32(rx_size); 331bc53d35aSJeremy Kerr hdr->tx_offset = htobe32(tx_offset); 332bc53d35aSJeremy Kerr hdr->tx_size = htobe32(tx_size); 333bc53d35aSJeremy Kerr 334bc53d35aSJeremy Kerr if (!lpc_direct(astlpc)) 335bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, hdr, 0, sizeof(*hdr)); 336672c8852SJeremy Kerr 337672c8852SJeremy Kerr /* set status indicating that the BMC is now active */ 338672c8852SJeremy Kerr status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF; 339bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, 340bc53d35aSJeremy Kerr MCTP_ASTLPC_KCS_REG_STATUS, status); 341bc53d35aSJeremy Kerr if (rc) { 342672c8852SJeremy Kerr mctp_prwarn("KCS write failed"); 343672c8852SJeremy Kerr } 344672c8852SJeremy Kerr 345672c8852SJeremy Kerr return rc; 346672c8852SJeremy Kerr } 347672c8852SJeremy Kerr 3488081bebaSJeremy Kerr static int mctp_binding_astlpc_start(struct mctp_binding *b) 3498081bebaSJeremy Kerr { 3508081bebaSJeremy Kerr struct mctp_binding_astlpc *astlpc = container_of(b, 3518081bebaSJeremy Kerr struct mctp_binding_astlpc, binding); 3528081bebaSJeremy Kerr 3538081bebaSJeremy Kerr return mctp_astlpc_init_bmc(astlpc); 3548081bebaSJeremy Kerr } 3558081bebaSJeremy Kerr 356bc53d35aSJeremy Kerr /* allocate and basic initialisation */ 357bc53d35aSJeremy Kerr static struct mctp_binding_astlpc *__mctp_astlpc_init(void) 358bc53d35aSJeremy Kerr { 359bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc; 360bc53d35aSJeremy Kerr 361bc53d35aSJeremy Kerr astlpc = __mctp_alloc(sizeof(*astlpc)); 362bc53d35aSJeremy Kerr memset(astlpc, 0, sizeof(*astlpc)); 363bc53d35aSJeremy Kerr astlpc->binding.name = "astlpc"; 364bc53d35aSJeremy Kerr astlpc->binding.version = 1; 365bc53d35aSJeremy Kerr astlpc->binding.tx = mctp_binding_astlpc_tx; 3668081bebaSJeremy Kerr astlpc->binding.start = mctp_binding_astlpc_start; 36773c268e4SAndrew Jeffery astlpc->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU); 368bc53d35aSJeremy Kerr astlpc->binding.pkt_pad = 0; 369bc53d35aSJeremy Kerr astlpc->lpc_map = NULL; 370bc53d35aSJeremy Kerr 371bc53d35aSJeremy Kerr return astlpc; 372bc53d35aSJeremy Kerr } 373bc53d35aSJeremy Kerr 3743b36d17cSJeremy Kerr struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b) 3753b36d17cSJeremy Kerr { 3763b36d17cSJeremy Kerr return &b->binding; 3773b36d17cSJeremy Kerr } 3783b36d17cSJeremy Kerr 379bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_ops( 380a0452495SAndrew Jeffery const struct mctp_binding_astlpc_ops *ops, 381bc53d35aSJeremy Kerr void *ops_data, void *lpc_map) 382bc53d35aSJeremy Kerr { 383bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc; 384bc53d35aSJeremy Kerr 385bc53d35aSJeremy Kerr astlpc = __mctp_astlpc_init(); 386bc53d35aSJeremy Kerr if (!astlpc) 387bc53d35aSJeremy Kerr return NULL; 388bc53d35aSJeremy Kerr 389bc53d35aSJeremy Kerr memcpy(&astlpc->ops, ops, sizeof(astlpc->ops)); 390bc53d35aSJeremy Kerr astlpc->ops_data = ops_data; 391bc53d35aSJeremy Kerr astlpc->lpc_map = lpc_map; 392bc53d35aSJeremy Kerr 393bc53d35aSJeremy Kerr /* In indirect mode, we keep a separate buffer of header data. 394bc53d35aSJeremy Kerr * We need to sync this through the lpc_read/lpc_write ops. 395bc53d35aSJeremy Kerr */ 396bc53d35aSJeremy Kerr if (!astlpc->lpc_map) 397bc53d35aSJeremy Kerr astlpc->priv_hdr = __mctp_alloc(sizeof(*astlpc->priv_hdr)); 398bc53d35aSJeremy Kerr 399bc53d35aSJeremy Kerr return astlpc; 400bc53d35aSJeremy Kerr } 401bc53d35aSJeremy Kerr 4024663f67cSAndrew Jeffery void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc) 4034663f67cSAndrew Jeffery { 4044663f67cSAndrew Jeffery if (astlpc->priv_hdr) 4054663f67cSAndrew Jeffery __mctp_free(astlpc->priv_hdr); 4064663f67cSAndrew Jeffery __mctp_free(astlpc); 4074663f67cSAndrew Jeffery } 4084663f67cSAndrew Jeffery 409b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO 410bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc) 411672c8852SJeremy Kerr { 412672c8852SJeremy Kerr struct aspeed_lpc_ctrl_mapping map = { 413672c8852SJeremy Kerr .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY, 414672c8852SJeremy Kerr .window_id = 0, /* There's only one */ 415672c8852SJeremy Kerr .flags = 0, 416672c8852SJeremy Kerr .addr = 0, 417672c8852SJeremy Kerr .offset = 0, 418672c8852SJeremy Kerr .size = 0 419672c8852SJeremy Kerr }; 420672c8852SJeremy Kerr int fd, rc; 421672c8852SJeremy Kerr 422672c8852SJeremy Kerr fd = open(lpc_path, O_RDWR | O_SYNC); 423672c8852SJeremy Kerr if (fd < 0) { 424672c8852SJeremy Kerr mctp_prwarn("LPC open (%s) failed", lpc_path); 425672c8852SJeremy Kerr return -1; 426672c8852SJeremy Kerr } 427672c8852SJeremy Kerr 428672c8852SJeremy Kerr rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map); 429672c8852SJeremy Kerr if (rc) { 430672c8852SJeremy Kerr mctp_prwarn("LPC GET_SIZE failed"); 431672c8852SJeremy Kerr close(fd); 432672c8852SJeremy Kerr return -1; 433672c8852SJeremy Kerr } 434672c8852SJeremy Kerr 435672c8852SJeremy Kerr astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE, 436672c8852SJeremy Kerr MAP_SHARED, fd, 0); 437672c8852SJeremy Kerr if (astlpc->lpc_map_base == MAP_FAILED) { 438672c8852SJeremy Kerr mctp_prwarn("LPC mmap failed"); 439672c8852SJeremy Kerr rc = -1; 440672c8852SJeremy Kerr } else { 441672c8852SJeremy Kerr astlpc->lpc_map = astlpc->lpc_map_base + 442672c8852SJeremy Kerr map.size - LPC_WIN_SIZE; 443672c8852SJeremy Kerr } 444672c8852SJeremy Kerr 445672c8852SJeremy Kerr close(fd); 446672c8852SJeremy Kerr 447672c8852SJeremy Kerr return rc; 448672c8852SJeremy Kerr } 449672c8852SJeremy Kerr 450bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc) 451672c8852SJeremy Kerr { 452672c8852SJeremy Kerr astlpc->kcs_fd = open(kcs_path, O_RDWR); 453672c8852SJeremy Kerr if (astlpc->kcs_fd < 0) 454672c8852SJeremy Kerr return -1; 455672c8852SJeremy Kerr 456672c8852SJeremy Kerr return 0; 457672c8852SJeremy Kerr } 458672c8852SJeremy Kerr 459bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_read(void *arg, 460bc53d35aSJeremy Kerr enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val) 461bc53d35aSJeremy Kerr { 462bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc = arg; 463bc53d35aSJeremy Kerr off_t offset = reg; 464bc53d35aSJeremy Kerr int rc; 465bc53d35aSJeremy Kerr 466bc53d35aSJeremy Kerr rc = pread(astlpc->kcs_fd, val, 1, offset); 467bc53d35aSJeremy Kerr 468bc53d35aSJeremy Kerr return rc == 1 ? 0 : -1; 469bc53d35aSJeremy Kerr } 470bc53d35aSJeremy Kerr 471bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_write(void *arg, 472bc53d35aSJeremy Kerr enum mctp_binding_astlpc_kcs_reg reg, uint8_t val) 473bc53d35aSJeremy Kerr { 474bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc = arg; 475bc53d35aSJeremy Kerr off_t offset = reg; 476bc53d35aSJeremy Kerr int rc; 477bc53d35aSJeremy Kerr 478bc53d35aSJeremy Kerr rc = pwrite(astlpc->kcs_fd, &val, 1, offset); 479bc53d35aSJeremy Kerr 480bc53d35aSJeremy Kerr return rc == 1 ? 0 : -1; 481bc53d35aSJeremy Kerr } 482bc53d35aSJeremy Kerr 483bc53d35aSJeremy Kerr int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc) 484bc53d35aSJeremy Kerr { 485bc53d35aSJeremy Kerr return astlpc->kcs_fd; 486bc53d35aSJeremy Kerr } 487bc53d35aSJeremy Kerr 488bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void) 489672c8852SJeremy Kerr { 490672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc; 491672c8852SJeremy Kerr int rc; 492672c8852SJeremy Kerr 493bc53d35aSJeremy Kerr astlpc = __mctp_astlpc_init(); 494bc53d35aSJeremy Kerr if (!astlpc) 495bc53d35aSJeremy Kerr return NULL; 496672c8852SJeremy Kerr 497bc53d35aSJeremy Kerr /* Set internal operations for kcs. We use direct accesses to the lpc 498bc53d35aSJeremy Kerr * map area */ 499bc53d35aSJeremy Kerr astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read; 500bc53d35aSJeremy Kerr astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write; 501bc53d35aSJeremy Kerr astlpc->ops_data = astlpc; 502bc53d35aSJeremy Kerr 503bc53d35aSJeremy Kerr rc = mctp_astlpc_init_fileio_lpc(astlpc); 504672c8852SJeremy Kerr if (rc) { 505672c8852SJeremy Kerr free(astlpc); 506672c8852SJeremy Kerr return NULL; 507672c8852SJeremy Kerr } 508672c8852SJeremy Kerr 509bc53d35aSJeremy Kerr rc = mctp_astlpc_init_fileio_kcs(astlpc); 510672c8852SJeremy Kerr if (rc) { 511672c8852SJeremy Kerr free(astlpc); 512672c8852SJeremy Kerr return NULL; 513672c8852SJeremy Kerr } 514672c8852SJeremy Kerr 515672c8852SJeremy Kerr return astlpc; 516672c8852SJeremy Kerr } 51792a10a6bSJeremy Kerr #else 51892a10a6bSJeremy Kerr struct mctp_binding_astlpc * __attribute__((const)) 51992a10a6bSJeremy Kerr mctp_astlpc_init_fileio(void) 52092a10a6bSJeremy Kerr { 52109e07497SAndrew Jeffery mctp_prerr("Missing support for file IO"); 52292a10a6bSJeremy Kerr return NULL; 52392a10a6bSJeremy Kerr } 524672c8852SJeremy Kerr 52592a10a6bSJeremy Kerr int __attribute__((const)) mctp_astlpc_get_fd( 52692a10a6bSJeremy Kerr struct mctp_binding_astlpc *astlpc __attribute__((unused))) 52792a10a6bSJeremy Kerr { 52809e07497SAndrew Jeffery mctp_prerr("Missing support for file IO"); 52992a10a6bSJeremy Kerr return -1; 53092a10a6bSJeremy Kerr } 53192a10a6bSJeremy Kerr #endif 532