13d36ee2eSJeremy Kerr /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2672c8852SJeremy Kerr 3*3286f176SAndrew Jeffery #if HAVE_CONFIG_H 4*3286f176SAndrew Jeffery #include "config.h" 5*3286f176SAndrew Jeffery #endif 6*3286f176SAndrew Jeffery 7*3286f176SAndrew Jeffery #if HAVE_ENDIAN_H 892a10a6bSJeremy Kerr #include <endian.h> 9*3286f176SAndrew Jeffery #endif 10*3286f176SAndrew Jeffery 11*3286f176SAndrew 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" 24672c8852SJeremy Kerr 25b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO 2692a10a6bSJeremy Kerr 27c6f676d1SJeremy Kerr #include <unistd.h> 2892a10a6bSJeremy Kerr #include <fcntl.h> 2992a10a6bSJeremy Kerr #include <sys/ioctl.h> 3092a10a6bSJeremy Kerr #include <sys/mman.h> 3192a10a6bSJeremy Kerr #include <linux/aspeed-lpc-ctrl.h> 3292a10a6bSJeremy Kerr 3392a10a6bSJeremy Kerr /* kernel interface */ 3492a10a6bSJeremy Kerr static const char *kcs_path = "/dev/mctp0"; 3592a10a6bSJeremy Kerr static const char *lpc_path = "/dev/aspeed-lpc-ctrl"; 3692a10a6bSJeremy Kerr 3792a10a6bSJeremy Kerr #endif 3892a10a6bSJeremy Kerr 39672c8852SJeremy Kerr struct mctp_binding_astlpc { 40672c8852SJeremy Kerr struct mctp_binding binding; 41bc53d35aSJeremy Kerr 42672c8852SJeremy Kerr union { 43672c8852SJeremy Kerr void *lpc_map; 44672c8852SJeremy Kerr struct mctp_lpcmap_hdr *lpc_hdr; 45672c8852SJeremy Kerr }; 46bc53d35aSJeremy Kerr 47bc53d35aSJeremy Kerr /* direct ops data */ 48bc53d35aSJeremy Kerr struct mctp_binding_astlpc_ops ops; 49bc53d35aSJeremy Kerr void *ops_data; 50bc53d35aSJeremy Kerr struct mctp_lpcmap_hdr *priv_hdr; 51bc53d35aSJeremy Kerr 52bc53d35aSJeremy Kerr /* fileio ops data */ 53bc53d35aSJeremy Kerr void *lpc_map_base; 54672c8852SJeremy Kerr int kcs_fd; 55672c8852SJeremy Kerr uint8_t kcs_status; 56672c8852SJeremy Kerr 57672c8852SJeremy Kerr bool running; 58672c8852SJeremy Kerr 59672c8852SJeremy Kerr /* temporary transmit buffer */ 60672c8852SJeremy Kerr uint8_t txbuf[256]; 61672c8852SJeremy Kerr }; 62672c8852SJeremy Kerr 63672c8852SJeremy Kerr #ifndef container_of 64672c8852SJeremy Kerr #define container_of(ptr, type, member) \ 65672c8852SJeremy Kerr (type *)((char *)(ptr) - (char *)&((type *)0)->member) 66672c8852SJeremy Kerr #endif 67672c8852SJeremy Kerr 68672c8852SJeremy Kerr #define binding_to_astlpc(b) \ 69672c8852SJeremy Kerr container_of(b, struct mctp_binding_astlpc, binding) 70672c8852SJeremy Kerr 71672c8852SJeremy Kerr #define MCTP_MAGIC 0x4d435450 72672c8852SJeremy Kerr #define BMC_VER_MIN 1 73672c8852SJeremy Kerr #define BMC_VER_CUR 1 74672c8852SJeremy Kerr 75672c8852SJeremy Kerr struct mctp_lpcmap_hdr { 76672c8852SJeremy Kerr uint32_t magic; 77672c8852SJeremy Kerr 78672c8852SJeremy Kerr uint16_t bmc_ver_min; 79672c8852SJeremy Kerr uint16_t bmc_ver_cur; 80672c8852SJeremy Kerr uint16_t host_ver_min; 81672c8852SJeremy Kerr uint16_t host_ver_cur; 82672c8852SJeremy Kerr uint16_t negotiated_ver; 83672c8852SJeremy Kerr uint16_t pad0; 84672c8852SJeremy Kerr 85672c8852SJeremy Kerr uint32_t rx_offset; 86672c8852SJeremy Kerr uint32_t rx_size; 87672c8852SJeremy Kerr uint32_t tx_offset; 88672c8852SJeremy Kerr uint32_t tx_size; 89672c8852SJeremy Kerr } __attribute__((packed)); 90672c8852SJeremy Kerr 91672c8852SJeremy Kerr /* layout of TX/RX areas */ 92672c8852SJeremy Kerr static const uint32_t rx_offset = 0x100; 93672c8852SJeremy Kerr static const uint32_t rx_size = 0x100; 94672c8852SJeremy Kerr static const uint32_t tx_offset = 0x200; 95672c8852SJeremy Kerr static const uint32_t tx_size = 0x100; 96672c8852SJeremy Kerr 97672c8852SJeremy Kerr #define LPC_WIN_SIZE (1 * 1024 * 1024) 98672c8852SJeremy Kerr 99672c8852SJeremy Kerr enum { 100672c8852SJeremy Kerr KCS_REG_DATA = 0, 101672c8852SJeremy Kerr KCS_REG_STATUS = 1, 102672c8852SJeremy Kerr }; 103672c8852SJeremy Kerr 104672c8852SJeremy Kerr #define KCS_STATUS_BMC_READY 0x80 105672c8852SJeremy Kerr #define KCS_STATUS_CHANNEL_ACTIVE 0x40 106672c8852SJeremy Kerr #define KCS_STATUS_IBF 0x02 107672c8852SJeremy Kerr #define KCS_STATUS_OBF 0x01 108672c8852SJeremy Kerr 109bc53d35aSJeremy Kerr static bool lpc_direct(struct mctp_binding_astlpc *astlpc) 110bc53d35aSJeremy Kerr { 111bc53d35aSJeremy Kerr return astlpc->lpc_map != NULL; 112bc53d35aSJeremy Kerr } 113bc53d35aSJeremy Kerr 114672c8852SJeremy Kerr static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc, 115672c8852SJeremy Kerr uint8_t status) 116672c8852SJeremy Kerr { 117bc53d35aSJeremy Kerr uint8_t data; 118672c8852SJeremy Kerr int rc; 119672c8852SJeremy Kerr 1201a4b55acSJeremy Kerr /* Since we're setting the status register, we want the other endpoint 1211a4b55acSJeremy Kerr * to be interrupted. However, some hardware may only raise a host-side 1221a4b55acSJeremy Kerr * interrupt on an ODR event. 1231a4b55acSJeremy Kerr * So, write a dummy value of 0xff to ODR, which will ensure that an 1241a4b55acSJeremy Kerr * interrupt is triggered, and can be ignored by the host. 1251a4b55acSJeremy Kerr */ 126bc53d35aSJeremy Kerr data = 0xff; 127bc53d35aSJeremy Kerr status |= KCS_STATUS_OBF; 1281a4b55acSJeremy Kerr 129c84fd56dSAndrew Jeffery rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 130c84fd56dSAndrew Jeffery status); 131c84fd56dSAndrew Jeffery if (rc) { 132c84fd56dSAndrew Jeffery mctp_prwarn("KCS status write failed"); 133c84fd56dSAndrew Jeffery return -1; 134c84fd56dSAndrew Jeffery } 135c84fd56dSAndrew Jeffery 136bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 137bc53d35aSJeremy Kerr data); 138bc53d35aSJeremy Kerr if (rc) { 139bc53d35aSJeremy Kerr mctp_prwarn("KCS dummy data write failed"); 140bc53d35aSJeremy Kerr return -1; 141bc53d35aSJeremy Kerr } 142bc53d35aSJeremy Kerr 143672c8852SJeremy Kerr return 0; 144672c8852SJeremy Kerr } 145672c8852SJeremy Kerr 146672c8852SJeremy Kerr static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc, 147672c8852SJeremy Kerr uint8_t data) 148672c8852SJeremy Kerr { 149672c8852SJeremy Kerr uint8_t status; 150672c8852SJeremy Kerr int rc; 151672c8852SJeremy Kerr 152672c8852SJeremy Kerr for (;;) { 153bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, 154bc53d35aSJeremy Kerr MCTP_ASTLPC_KCS_REG_STATUS, &status); 1551b27fe87SAndrew Jeffery if (rc) { 156182204e3SAndrew Jeffery mctp_prwarn("KCS status read failed"); 157672c8852SJeremy Kerr return -1; 158672c8852SJeremy Kerr } 159672c8852SJeremy Kerr if (!(status & KCS_STATUS_OBF)) 160672c8852SJeremy Kerr break; 161672c8852SJeremy Kerr /* todo: timeout */ 162672c8852SJeremy Kerr } 163672c8852SJeremy Kerr 164bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 165bc53d35aSJeremy Kerr data); 166bc53d35aSJeremy Kerr if (rc) { 167672c8852SJeremy Kerr mctp_prwarn("KCS data write failed"); 168672c8852SJeremy Kerr return -1; 169672c8852SJeremy Kerr } 170672c8852SJeremy Kerr 171672c8852SJeremy Kerr return 0; 172672c8852SJeremy Kerr } 173672c8852SJeremy Kerr 174672c8852SJeremy Kerr static int mctp_binding_astlpc_tx(struct mctp_binding *b, 175672c8852SJeremy Kerr struct mctp_pktbuf *pkt) 176672c8852SJeremy Kerr { 177672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b); 178672c8852SJeremy Kerr uint32_t len; 179edfe383fSAndrew Jeffery struct mctp_hdr *hdr; 180672c8852SJeremy Kerr 181edfe383fSAndrew Jeffery hdr = mctp_pktbuf_hdr(pkt); 182672c8852SJeremy Kerr len = mctp_pktbuf_size(pkt); 183edfe383fSAndrew Jeffery 184edfe383fSAndrew Jeffery mctp_prdebug("%s: Transmitting %"PRIu32"-byte packet (%hhu, %hhu, 0x%hhx)", 185edfe383fSAndrew Jeffery __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag); 186edfe383fSAndrew Jeffery 187672c8852SJeremy Kerr if (len > rx_size - 4) { 188672c8852SJeremy Kerr mctp_prwarn("invalid TX len 0x%x", len); 189672c8852SJeremy Kerr return -1; 190672c8852SJeremy Kerr } 191672c8852SJeremy Kerr 192bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 193672c8852SJeremy Kerr *(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len); 194bc53d35aSJeremy Kerr memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt), 195bc53d35aSJeremy Kerr len); 196bc53d35aSJeremy Kerr } else { 197bc53d35aSJeremy Kerr uint32_t tmp = htobe32(len); 198bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, &tmp, rx_offset, 199bc53d35aSJeremy Kerr sizeof(tmp)); 200bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 201bc53d35aSJeremy Kerr rx_offset + 4, len); 202bc53d35aSJeremy Kerr } 203672c8852SJeremy Kerr 204672c8852SJeremy Kerr mctp_binding_set_tx_enabled(b, false); 205672c8852SJeremy Kerr 206672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x1); 207672c8852SJeremy Kerr return 0; 208672c8852SJeremy Kerr } 209672c8852SJeremy Kerr 210672c8852SJeremy Kerr static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc) 211672c8852SJeremy Kerr { 212672c8852SJeremy Kerr /* todo: actual version negotiation */ 213bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 214672c8852SJeremy Kerr astlpc->lpc_hdr->negotiated_ver = htobe16(1); 215bc53d35aSJeremy Kerr } else { 216bc53d35aSJeremy Kerr uint16_t ver = htobe16(1); 217bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, &ver, 218bc53d35aSJeremy Kerr offsetof(struct mctp_lpcmap_hdr, 219bc53d35aSJeremy Kerr negotiated_ver), 220bc53d35aSJeremy Kerr sizeof(ver)); 221bc53d35aSJeremy Kerr } 222672c8852SJeremy Kerr mctp_astlpc_kcs_set_status(astlpc, 223672c8852SJeremy Kerr KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE | 224672c8852SJeremy Kerr KCS_STATUS_OBF); 225672c8852SJeremy Kerr 226672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 227672c8852SJeremy Kerr } 228672c8852SJeremy Kerr 229672c8852SJeremy Kerr static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc) 230672c8852SJeremy Kerr { 231672c8852SJeremy Kerr struct mctp_pktbuf *pkt; 232672c8852SJeremy Kerr uint32_t len; 233672c8852SJeremy Kerr 234bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 235bc53d35aSJeremy Kerr len = *(uint32_t *)(astlpc->lpc_map + tx_offset); 236bc53d35aSJeremy Kerr } else { 237bc53d35aSJeremy Kerr astlpc->ops.lpc_read(astlpc->ops_data, &len, 238bc53d35aSJeremy Kerr tx_offset, sizeof(len)); 239bc53d35aSJeremy Kerr } 240bc53d35aSJeremy Kerr len = be32toh(len); 241bc53d35aSJeremy Kerr 242672c8852SJeremy Kerr if (len > tx_size - 4) { 243672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 244672c8852SJeremy Kerr return; 245672c8852SJeremy Kerr } 246672c8852SJeremy Kerr 247df15f7e9SJeremy Kerr if (len > astlpc->binding.pkt_size) { 248672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 249672c8852SJeremy Kerr return; 250672c8852SJeremy Kerr } 251672c8852SJeremy Kerr 252df15f7e9SJeremy Kerr pkt = mctp_pktbuf_alloc(&astlpc->binding, len); 253672c8852SJeremy Kerr if (!pkt) 254672c8852SJeremy Kerr goto out_complete; 255672c8852SJeremy Kerr 256bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 257bc53d35aSJeremy Kerr memcpy(mctp_pktbuf_hdr(pkt), 258bc53d35aSJeremy Kerr astlpc->lpc_map + tx_offset + 4, len); 259bc53d35aSJeremy Kerr } else { 260bc53d35aSJeremy Kerr astlpc->ops.lpc_read(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 261bc53d35aSJeremy Kerr tx_offset + 4, len); 262bc53d35aSJeremy Kerr } 263672c8852SJeremy Kerr 264672c8852SJeremy Kerr mctp_bus_rx(&astlpc->binding, pkt); 265672c8852SJeremy Kerr 266672c8852SJeremy Kerr out_complete: 267672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x2); 268672c8852SJeremy Kerr } 269672c8852SJeremy Kerr 270672c8852SJeremy Kerr static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc) 271672c8852SJeremy Kerr { 272672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 273672c8852SJeremy Kerr } 274672c8852SJeremy Kerr 275672c8852SJeremy Kerr int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc) 276672c8852SJeremy Kerr { 277bc53d35aSJeremy Kerr uint8_t status, data; 278672c8852SJeremy Kerr int rc; 279672c8852SJeremy Kerr 280bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 281bc53d35aSJeremy Kerr &status); 282bc53d35aSJeremy Kerr if (rc) { 283672c8852SJeremy Kerr mctp_prwarn("KCS read error"); 284672c8852SJeremy Kerr return -1; 285672c8852SJeremy Kerr } 286672c8852SJeremy Kerr 287edfe383fSAndrew Jeffery mctp_prdebug("%s: status: 0x%hhx", __func__, status); 288edfe383fSAndrew Jeffery 289bc53d35aSJeremy Kerr if (!(status & KCS_STATUS_IBF)) 290672c8852SJeremy Kerr return 0; 291672c8852SJeremy Kerr 292bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 293bc53d35aSJeremy Kerr &data); 294bc53d35aSJeremy Kerr if (rc) { 295bc53d35aSJeremy Kerr mctp_prwarn("KCS data read error"); 296bc53d35aSJeremy Kerr return -1; 297bc53d35aSJeremy Kerr } 298bc53d35aSJeremy Kerr 299edfe383fSAndrew Jeffery mctp_prdebug("%s: data: 0x%hhx", __func__, data); 300edfe383fSAndrew Jeffery 301672c8852SJeremy Kerr switch (data) { 302672c8852SJeremy Kerr case 0x0: 303672c8852SJeremy Kerr mctp_astlpc_init_channel(astlpc); 304672c8852SJeremy Kerr break; 305672c8852SJeremy Kerr case 0x1: 306672c8852SJeremy Kerr mctp_astlpc_rx_start(astlpc); 307672c8852SJeremy Kerr break; 308672c8852SJeremy Kerr case 0x2: 309672c8852SJeremy Kerr mctp_astlpc_tx_complete(astlpc); 310672c8852SJeremy Kerr break; 3111a4b55acSJeremy Kerr case 0xff: 3121a4b55acSJeremy Kerr /* reserved value for dummy data writes; do nothing */ 3131a4b55acSJeremy Kerr break; 314672c8852SJeremy Kerr default: 315672c8852SJeremy Kerr mctp_prwarn("unknown message 0x%x", data); 316672c8852SJeremy Kerr } 317672c8852SJeremy Kerr return 0; 318672c8852SJeremy Kerr } 319672c8852SJeremy Kerr 320672c8852SJeremy Kerr static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc) 321672c8852SJeremy Kerr { 322bc53d35aSJeremy Kerr struct mctp_lpcmap_hdr *hdr; 323672c8852SJeremy Kerr uint8_t status; 324672c8852SJeremy Kerr int rc; 325672c8852SJeremy Kerr 326bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) 327bc53d35aSJeremy Kerr hdr = astlpc->lpc_hdr; 328bc53d35aSJeremy Kerr else 329bc53d35aSJeremy Kerr hdr = astlpc->priv_hdr; 330672c8852SJeremy Kerr 331bc53d35aSJeremy Kerr hdr->magic = htobe32(MCTP_MAGIC); 332bc53d35aSJeremy Kerr hdr->bmc_ver_min = htobe16(BMC_VER_MIN); 333bc53d35aSJeremy Kerr hdr->bmc_ver_cur = htobe16(BMC_VER_CUR); 334bc53d35aSJeremy Kerr 335bc53d35aSJeremy Kerr hdr->rx_offset = htobe32(rx_offset); 336bc53d35aSJeremy Kerr hdr->rx_size = htobe32(rx_size); 337bc53d35aSJeremy Kerr hdr->tx_offset = htobe32(tx_offset); 338bc53d35aSJeremy Kerr hdr->tx_size = htobe32(tx_size); 339bc53d35aSJeremy Kerr 340bc53d35aSJeremy Kerr if (!lpc_direct(astlpc)) 341bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, hdr, 0, sizeof(*hdr)); 342672c8852SJeremy Kerr 343672c8852SJeremy Kerr /* set status indicating that the BMC is now active */ 344672c8852SJeremy Kerr status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF; 345bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, 346bc53d35aSJeremy Kerr MCTP_ASTLPC_KCS_REG_STATUS, status); 347bc53d35aSJeremy Kerr if (rc) { 348672c8852SJeremy Kerr mctp_prwarn("KCS write failed"); 349672c8852SJeremy Kerr } 350672c8852SJeremy Kerr 351672c8852SJeremy Kerr return rc; 352672c8852SJeremy Kerr } 353672c8852SJeremy Kerr 3548081bebaSJeremy Kerr static int mctp_binding_astlpc_start(struct mctp_binding *b) 3558081bebaSJeremy Kerr { 3568081bebaSJeremy Kerr struct mctp_binding_astlpc *astlpc = container_of(b, 3578081bebaSJeremy Kerr struct mctp_binding_astlpc, binding); 3588081bebaSJeremy Kerr 3598081bebaSJeremy Kerr return mctp_astlpc_init_bmc(astlpc); 3608081bebaSJeremy Kerr } 3618081bebaSJeremy Kerr 362bc53d35aSJeremy Kerr /* allocate and basic initialisation */ 363bc53d35aSJeremy Kerr static struct mctp_binding_astlpc *__mctp_astlpc_init(void) 364bc53d35aSJeremy Kerr { 365bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc; 366bc53d35aSJeremy Kerr 367bc53d35aSJeremy Kerr astlpc = __mctp_alloc(sizeof(*astlpc)); 368bc53d35aSJeremy Kerr memset(astlpc, 0, sizeof(*astlpc)); 369bc53d35aSJeremy Kerr astlpc->binding.name = "astlpc"; 370bc53d35aSJeremy Kerr astlpc->binding.version = 1; 371bc53d35aSJeremy Kerr astlpc->binding.tx = mctp_binding_astlpc_tx; 3728081bebaSJeremy Kerr astlpc->binding.start = mctp_binding_astlpc_start; 37373c268e4SAndrew Jeffery astlpc->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU); 374bc53d35aSJeremy Kerr astlpc->binding.pkt_pad = 0; 375bc53d35aSJeremy Kerr astlpc->lpc_map = NULL; 376bc53d35aSJeremy Kerr 377bc53d35aSJeremy Kerr return astlpc; 378bc53d35aSJeremy Kerr } 379bc53d35aSJeremy Kerr 3803b36d17cSJeremy Kerr struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b) 3813b36d17cSJeremy Kerr { 3823b36d17cSJeremy Kerr return &b->binding; 3833b36d17cSJeremy Kerr } 3843b36d17cSJeremy Kerr 385bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_ops( 386a0452495SAndrew Jeffery const struct mctp_binding_astlpc_ops *ops, 387bc53d35aSJeremy Kerr void *ops_data, void *lpc_map) 388bc53d35aSJeremy Kerr { 389bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc; 390bc53d35aSJeremy Kerr 391bc53d35aSJeremy Kerr astlpc = __mctp_astlpc_init(); 392bc53d35aSJeremy Kerr if (!astlpc) 393bc53d35aSJeremy Kerr return NULL; 394bc53d35aSJeremy Kerr 395bc53d35aSJeremy Kerr memcpy(&astlpc->ops, ops, sizeof(astlpc->ops)); 396bc53d35aSJeremy Kerr astlpc->ops_data = ops_data; 397bc53d35aSJeremy Kerr astlpc->lpc_map = lpc_map; 398bc53d35aSJeremy Kerr 399bc53d35aSJeremy Kerr /* In indirect mode, we keep a separate buffer of header data. 400bc53d35aSJeremy Kerr * We need to sync this through the lpc_read/lpc_write ops. 401bc53d35aSJeremy Kerr */ 402bc53d35aSJeremy Kerr if (!astlpc->lpc_map) 403bc53d35aSJeremy Kerr astlpc->priv_hdr = __mctp_alloc(sizeof(*astlpc->priv_hdr)); 404bc53d35aSJeremy Kerr 405bc53d35aSJeremy Kerr return astlpc; 406bc53d35aSJeremy Kerr } 407bc53d35aSJeremy Kerr 4084663f67cSAndrew Jeffery void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc) 4094663f67cSAndrew Jeffery { 4104663f67cSAndrew Jeffery if (astlpc->priv_hdr) 4114663f67cSAndrew Jeffery __mctp_free(astlpc->priv_hdr); 4124663f67cSAndrew Jeffery __mctp_free(astlpc); 4134663f67cSAndrew Jeffery } 4144663f67cSAndrew Jeffery 415b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO 416bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc) 417672c8852SJeremy Kerr { 418672c8852SJeremy Kerr struct aspeed_lpc_ctrl_mapping map = { 419672c8852SJeremy Kerr .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY, 420672c8852SJeremy Kerr .window_id = 0, /* There's only one */ 421672c8852SJeremy Kerr .flags = 0, 422672c8852SJeremy Kerr .addr = 0, 423672c8852SJeremy Kerr .offset = 0, 424672c8852SJeremy Kerr .size = 0 425672c8852SJeremy Kerr }; 426672c8852SJeremy Kerr int fd, rc; 427672c8852SJeremy Kerr 428672c8852SJeremy Kerr fd = open(lpc_path, O_RDWR | O_SYNC); 429672c8852SJeremy Kerr if (fd < 0) { 430672c8852SJeremy Kerr mctp_prwarn("LPC open (%s) failed", lpc_path); 431672c8852SJeremy Kerr return -1; 432672c8852SJeremy Kerr } 433672c8852SJeremy Kerr 434672c8852SJeremy Kerr rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map); 435672c8852SJeremy Kerr if (rc) { 436672c8852SJeremy Kerr mctp_prwarn("LPC GET_SIZE failed"); 437672c8852SJeremy Kerr close(fd); 438672c8852SJeremy Kerr return -1; 439672c8852SJeremy Kerr } 440672c8852SJeremy Kerr 441672c8852SJeremy Kerr astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE, 442672c8852SJeremy Kerr MAP_SHARED, fd, 0); 443672c8852SJeremy Kerr if (astlpc->lpc_map_base == MAP_FAILED) { 444672c8852SJeremy Kerr mctp_prwarn("LPC mmap failed"); 445672c8852SJeremy Kerr rc = -1; 446672c8852SJeremy Kerr } else { 447672c8852SJeremy Kerr astlpc->lpc_map = astlpc->lpc_map_base + 448672c8852SJeremy Kerr map.size - LPC_WIN_SIZE; 449672c8852SJeremy Kerr } 450672c8852SJeremy Kerr 451672c8852SJeremy Kerr close(fd); 452672c8852SJeremy Kerr 453672c8852SJeremy Kerr return rc; 454672c8852SJeremy Kerr } 455672c8852SJeremy Kerr 456bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc) 457672c8852SJeremy Kerr { 458672c8852SJeremy Kerr astlpc->kcs_fd = open(kcs_path, O_RDWR); 459672c8852SJeremy Kerr if (astlpc->kcs_fd < 0) 460672c8852SJeremy Kerr return -1; 461672c8852SJeremy Kerr 462672c8852SJeremy Kerr return 0; 463672c8852SJeremy Kerr } 464672c8852SJeremy Kerr 465bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_read(void *arg, 466bc53d35aSJeremy Kerr enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val) 467bc53d35aSJeremy Kerr { 468bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc = arg; 469bc53d35aSJeremy Kerr off_t offset = reg; 470bc53d35aSJeremy Kerr int rc; 471bc53d35aSJeremy Kerr 472bc53d35aSJeremy Kerr rc = pread(astlpc->kcs_fd, val, 1, offset); 473bc53d35aSJeremy Kerr 474bc53d35aSJeremy Kerr return rc == 1 ? 0 : -1; 475bc53d35aSJeremy Kerr } 476bc53d35aSJeremy Kerr 477bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_write(void *arg, 478bc53d35aSJeremy Kerr enum mctp_binding_astlpc_kcs_reg reg, uint8_t val) 479bc53d35aSJeremy Kerr { 480bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc = arg; 481bc53d35aSJeremy Kerr off_t offset = reg; 482bc53d35aSJeremy Kerr int rc; 483bc53d35aSJeremy Kerr 484bc53d35aSJeremy Kerr rc = pwrite(astlpc->kcs_fd, &val, 1, offset); 485bc53d35aSJeremy Kerr 486bc53d35aSJeremy Kerr return rc == 1 ? 0 : -1; 487bc53d35aSJeremy Kerr } 488bc53d35aSJeremy Kerr 489bc53d35aSJeremy Kerr int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc) 490bc53d35aSJeremy Kerr { 491bc53d35aSJeremy Kerr return astlpc->kcs_fd; 492bc53d35aSJeremy Kerr } 493bc53d35aSJeremy Kerr 494bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void) 495672c8852SJeremy Kerr { 496672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc; 497672c8852SJeremy Kerr int rc; 498672c8852SJeremy Kerr 499bc53d35aSJeremy Kerr astlpc = __mctp_astlpc_init(); 500bc53d35aSJeremy Kerr if (!astlpc) 501bc53d35aSJeremy Kerr return NULL; 502672c8852SJeremy Kerr 503bc53d35aSJeremy Kerr /* Set internal operations for kcs. We use direct accesses to the lpc 504bc53d35aSJeremy Kerr * map area */ 505bc53d35aSJeremy Kerr astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read; 506bc53d35aSJeremy Kerr astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write; 507bc53d35aSJeremy Kerr astlpc->ops_data = astlpc; 508bc53d35aSJeremy Kerr 509bc53d35aSJeremy Kerr rc = mctp_astlpc_init_fileio_lpc(astlpc); 510672c8852SJeremy Kerr if (rc) { 511672c8852SJeremy Kerr free(astlpc); 512672c8852SJeremy Kerr return NULL; 513672c8852SJeremy Kerr } 514672c8852SJeremy Kerr 515bc53d35aSJeremy Kerr rc = mctp_astlpc_init_fileio_kcs(astlpc); 516672c8852SJeremy Kerr if (rc) { 517672c8852SJeremy Kerr free(astlpc); 518672c8852SJeremy Kerr return NULL; 519672c8852SJeremy Kerr } 520672c8852SJeremy Kerr 521672c8852SJeremy Kerr return astlpc; 522672c8852SJeremy Kerr } 52392a10a6bSJeremy Kerr #else 52492a10a6bSJeremy Kerr struct mctp_binding_astlpc * __attribute__((const)) 52592a10a6bSJeremy Kerr mctp_astlpc_init_fileio(void) 52692a10a6bSJeremy Kerr { 52709e07497SAndrew Jeffery mctp_prerr("Missing support for file IO"); 52892a10a6bSJeremy Kerr return NULL; 52992a10a6bSJeremy Kerr } 530672c8852SJeremy Kerr 53192a10a6bSJeremy Kerr int __attribute__((const)) mctp_astlpc_get_fd( 53292a10a6bSJeremy Kerr struct mctp_binding_astlpc *astlpc __attribute__((unused))) 53392a10a6bSJeremy Kerr { 53409e07497SAndrew Jeffery mctp_prerr("Missing support for file IO"); 53592a10a6bSJeremy Kerr return -1; 53692a10a6bSJeremy Kerr } 53792a10a6bSJeremy Kerr #endif 538