13d36ee2eSJeremy Kerr /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2672c8852SJeremy Kerr 3672c8852SJeremy Kerr #include <assert.h> 492a10a6bSJeremy Kerr #include <endian.h> 559c6a5c9SAndrew Jeffery #include <err.h> 6*edfe383fSAndrew Jeffery #include <inttypes.h> 7672c8852SJeremy Kerr #include <stdbool.h> 8672c8852SJeremy Kerr #include <stdlib.h> 9672c8852SJeremy Kerr #include <string.h> 10672c8852SJeremy Kerr 11672c8852SJeremy Kerr #define pr_fmt(x) "astlpc: " x 12672c8852SJeremy Kerr 1359c6a5c9SAndrew Jeffery #if HAVE_CONFIG_H 1459c6a5c9SAndrew Jeffery #include "config.h" 1559c6a5c9SAndrew Jeffery #endif 1659c6a5c9SAndrew Jeffery 17672c8852SJeremy Kerr #include "libmctp.h" 18672c8852SJeremy Kerr #include "libmctp-alloc.h" 19672c8852SJeremy Kerr #include "libmctp-log.h" 20672c8852SJeremy Kerr #include "libmctp-astlpc.h" 21672c8852SJeremy Kerr 22b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO 2392a10a6bSJeremy Kerr 24c6f676d1SJeremy Kerr #include <unistd.h> 2592a10a6bSJeremy Kerr #include <fcntl.h> 2692a10a6bSJeremy Kerr #include <sys/ioctl.h> 2792a10a6bSJeremy Kerr #include <sys/mman.h> 2892a10a6bSJeremy Kerr #include <linux/aspeed-lpc-ctrl.h> 2992a10a6bSJeremy Kerr 3092a10a6bSJeremy Kerr /* kernel interface */ 3192a10a6bSJeremy Kerr static const char *kcs_path = "/dev/mctp0"; 3292a10a6bSJeremy Kerr static const char *lpc_path = "/dev/aspeed-lpc-ctrl"; 3392a10a6bSJeremy Kerr 3492a10a6bSJeremy Kerr #endif 3592a10a6bSJeremy Kerr 36672c8852SJeremy Kerr struct mctp_binding_astlpc { 37672c8852SJeremy Kerr struct mctp_binding binding; 38bc53d35aSJeremy Kerr 39672c8852SJeremy Kerr union { 40672c8852SJeremy Kerr void *lpc_map; 41672c8852SJeremy Kerr struct mctp_lpcmap_hdr *lpc_hdr; 42672c8852SJeremy Kerr }; 43bc53d35aSJeremy Kerr 44bc53d35aSJeremy Kerr /* direct ops data */ 45bc53d35aSJeremy Kerr struct mctp_binding_astlpc_ops ops; 46bc53d35aSJeremy Kerr void *ops_data; 47bc53d35aSJeremy Kerr struct mctp_lpcmap_hdr *priv_hdr; 48bc53d35aSJeremy Kerr 49bc53d35aSJeremy Kerr /* fileio ops data */ 50bc53d35aSJeremy Kerr void *lpc_map_base; 51672c8852SJeremy Kerr int kcs_fd; 52672c8852SJeremy Kerr uint8_t kcs_status; 53672c8852SJeremy Kerr 54672c8852SJeremy Kerr bool running; 55672c8852SJeremy Kerr 56672c8852SJeremy Kerr /* temporary transmit buffer */ 57672c8852SJeremy Kerr uint8_t txbuf[256]; 58672c8852SJeremy Kerr }; 59672c8852SJeremy Kerr 60672c8852SJeremy Kerr #ifndef container_of 61672c8852SJeremy Kerr #define container_of(ptr, type, member) \ 62672c8852SJeremy Kerr (type *)((char *)(ptr) - (char *)&((type *)0)->member) 63672c8852SJeremy Kerr #endif 64672c8852SJeremy Kerr 65672c8852SJeremy Kerr #define binding_to_astlpc(b) \ 66672c8852SJeremy Kerr container_of(b, struct mctp_binding_astlpc, binding) 67672c8852SJeremy Kerr 68672c8852SJeremy Kerr #define MCTP_MAGIC 0x4d435450 69672c8852SJeremy Kerr #define BMC_VER_MIN 1 70672c8852SJeremy Kerr #define BMC_VER_CUR 1 71672c8852SJeremy Kerr 72672c8852SJeremy Kerr struct mctp_lpcmap_hdr { 73672c8852SJeremy Kerr uint32_t magic; 74672c8852SJeremy Kerr 75672c8852SJeremy Kerr uint16_t bmc_ver_min; 76672c8852SJeremy Kerr uint16_t bmc_ver_cur; 77672c8852SJeremy Kerr uint16_t host_ver_min; 78672c8852SJeremy Kerr uint16_t host_ver_cur; 79672c8852SJeremy Kerr uint16_t negotiated_ver; 80672c8852SJeremy Kerr uint16_t pad0; 81672c8852SJeremy Kerr 82672c8852SJeremy Kerr uint32_t rx_offset; 83672c8852SJeremy Kerr uint32_t rx_size; 84672c8852SJeremy Kerr uint32_t tx_offset; 85672c8852SJeremy Kerr uint32_t tx_size; 86672c8852SJeremy Kerr } __attribute__((packed)); 87672c8852SJeremy Kerr 88672c8852SJeremy Kerr /* layout of TX/RX areas */ 89672c8852SJeremy Kerr static const uint32_t rx_offset = 0x100; 90672c8852SJeremy Kerr static const uint32_t rx_size = 0x100; 91672c8852SJeremy Kerr static const uint32_t tx_offset = 0x200; 92672c8852SJeremy Kerr static const uint32_t tx_size = 0x100; 93672c8852SJeremy Kerr 94672c8852SJeremy Kerr #define LPC_WIN_SIZE (1 * 1024 * 1024) 95672c8852SJeremy Kerr 96672c8852SJeremy Kerr enum { 97672c8852SJeremy Kerr KCS_REG_DATA = 0, 98672c8852SJeremy Kerr KCS_REG_STATUS = 1, 99672c8852SJeremy Kerr }; 100672c8852SJeremy Kerr 101672c8852SJeremy Kerr #define KCS_STATUS_BMC_READY 0x80 102672c8852SJeremy Kerr #define KCS_STATUS_CHANNEL_ACTIVE 0x40 103672c8852SJeremy Kerr #define KCS_STATUS_IBF 0x02 104672c8852SJeremy Kerr #define KCS_STATUS_OBF 0x01 105672c8852SJeremy Kerr 106bc53d35aSJeremy Kerr static bool lpc_direct(struct mctp_binding_astlpc *astlpc) 107bc53d35aSJeremy Kerr { 108bc53d35aSJeremy Kerr return astlpc->lpc_map != NULL; 109bc53d35aSJeremy Kerr } 110bc53d35aSJeremy Kerr 111672c8852SJeremy Kerr static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc, 112672c8852SJeremy Kerr uint8_t status) 113672c8852SJeremy Kerr { 114bc53d35aSJeremy Kerr uint8_t data; 115672c8852SJeremy Kerr int rc; 116672c8852SJeremy Kerr 1171a4b55acSJeremy Kerr /* Since we're setting the status register, we want the other endpoint 1181a4b55acSJeremy Kerr * to be interrupted. However, some hardware may only raise a host-side 1191a4b55acSJeremy Kerr * interrupt on an ODR event. 1201a4b55acSJeremy Kerr * So, write a dummy value of 0xff to ODR, which will ensure that an 1211a4b55acSJeremy Kerr * interrupt is triggered, and can be ignored by the host. 1221a4b55acSJeremy Kerr */ 123bc53d35aSJeremy Kerr data = 0xff; 124bc53d35aSJeremy Kerr status |= KCS_STATUS_OBF; 1251a4b55acSJeremy Kerr 126bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 127bc53d35aSJeremy Kerr data); 128bc53d35aSJeremy Kerr if (rc) { 129bc53d35aSJeremy Kerr mctp_prwarn("KCS dummy data write failed"); 130bc53d35aSJeremy Kerr return -1; 131bc53d35aSJeremy Kerr } 132bc53d35aSJeremy Kerr 133bc53d35aSJeremy Kerr 134bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 135bc53d35aSJeremy Kerr status); 136bc53d35aSJeremy Kerr if (rc) { 137672c8852SJeremy Kerr mctp_prwarn("KCS status write failed"); 138672c8852SJeremy Kerr return -1; 139672c8852SJeremy Kerr } 140672c8852SJeremy Kerr return 0; 141672c8852SJeremy Kerr } 142672c8852SJeremy Kerr 143672c8852SJeremy Kerr static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc, 144672c8852SJeremy Kerr uint8_t data) 145672c8852SJeremy Kerr { 146672c8852SJeremy Kerr uint8_t status; 147672c8852SJeremy Kerr int rc; 148672c8852SJeremy Kerr 149672c8852SJeremy Kerr for (;;) { 150bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, 151bc53d35aSJeremy Kerr MCTP_ASTLPC_KCS_REG_STATUS, &status); 1521b27fe87SAndrew Jeffery if (rc) { 153182204e3SAndrew Jeffery mctp_prwarn("KCS status read failed"); 154672c8852SJeremy Kerr return -1; 155672c8852SJeremy Kerr } 156672c8852SJeremy Kerr if (!(status & KCS_STATUS_OBF)) 157672c8852SJeremy Kerr break; 158672c8852SJeremy Kerr /* todo: timeout */ 159672c8852SJeremy Kerr } 160672c8852SJeremy Kerr 161bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 162bc53d35aSJeremy Kerr data); 163bc53d35aSJeremy Kerr if (rc) { 164672c8852SJeremy Kerr mctp_prwarn("KCS data write failed"); 165672c8852SJeremy Kerr return -1; 166672c8852SJeremy Kerr } 167672c8852SJeremy Kerr 168672c8852SJeremy Kerr return 0; 169672c8852SJeremy Kerr } 170672c8852SJeremy Kerr 171672c8852SJeremy Kerr static int mctp_binding_astlpc_tx(struct mctp_binding *b, 172672c8852SJeremy Kerr struct mctp_pktbuf *pkt) 173672c8852SJeremy Kerr { 174672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b); 175672c8852SJeremy Kerr uint32_t len; 176*edfe383fSAndrew Jeffery struct mctp_hdr *hdr; 177672c8852SJeremy Kerr 178*edfe383fSAndrew Jeffery hdr = mctp_pktbuf_hdr(pkt); 179672c8852SJeremy Kerr len = mctp_pktbuf_size(pkt); 180*edfe383fSAndrew Jeffery 181*edfe383fSAndrew Jeffery mctp_prdebug("%s: Transmitting %"PRIu32"-byte packet (%hhu, %hhu, 0x%hhx)", 182*edfe383fSAndrew Jeffery __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag); 183*edfe383fSAndrew Jeffery 184672c8852SJeremy Kerr if (len > rx_size - 4) { 185672c8852SJeremy Kerr mctp_prwarn("invalid TX len 0x%x", len); 186672c8852SJeremy Kerr return -1; 187672c8852SJeremy Kerr } 188672c8852SJeremy Kerr 189bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 190672c8852SJeremy Kerr *(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len); 191bc53d35aSJeremy Kerr memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt), 192bc53d35aSJeremy Kerr len); 193bc53d35aSJeremy Kerr } else { 194bc53d35aSJeremy Kerr uint32_t tmp = htobe32(len); 195bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, &tmp, rx_offset, 196bc53d35aSJeremy Kerr sizeof(tmp)); 197bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 198bc53d35aSJeremy Kerr rx_offset + 4, len); 199bc53d35aSJeremy Kerr } 200672c8852SJeremy Kerr 201672c8852SJeremy Kerr mctp_binding_set_tx_enabled(b, false); 202672c8852SJeremy Kerr 203672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x1); 204672c8852SJeremy Kerr return 0; 205672c8852SJeremy Kerr } 206672c8852SJeremy Kerr 207672c8852SJeremy Kerr static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc) 208672c8852SJeremy Kerr { 209672c8852SJeremy Kerr /* todo: actual version negotiation */ 210bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 211672c8852SJeremy Kerr astlpc->lpc_hdr->negotiated_ver = htobe16(1); 212bc53d35aSJeremy Kerr } else { 213bc53d35aSJeremy Kerr uint16_t ver = htobe16(1); 214bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, &ver, 215bc53d35aSJeremy Kerr offsetof(struct mctp_lpcmap_hdr, 216bc53d35aSJeremy Kerr negotiated_ver), 217bc53d35aSJeremy Kerr sizeof(ver)); 218bc53d35aSJeremy Kerr } 219672c8852SJeremy Kerr mctp_astlpc_kcs_set_status(astlpc, 220672c8852SJeremy Kerr KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE | 221672c8852SJeremy Kerr KCS_STATUS_OBF); 222672c8852SJeremy Kerr 223672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 224672c8852SJeremy Kerr } 225672c8852SJeremy Kerr 226672c8852SJeremy Kerr static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc) 227672c8852SJeremy Kerr { 228672c8852SJeremy Kerr struct mctp_pktbuf *pkt; 229672c8852SJeremy Kerr uint32_t len; 230672c8852SJeremy Kerr 231bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 232bc53d35aSJeremy Kerr len = *(uint32_t *)(astlpc->lpc_map + tx_offset); 233bc53d35aSJeremy Kerr } else { 234bc53d35aSJeremy Kerr astlpc->ops.lpc_read(astlpc->ops_data, &len, 235bc53d35aSJeremy Kerr tx_offset, sizeof(len)); 236bc53d35aSJeremy Kerr } 237bc53d35aSJeremy Kerr len = be32toh(len); 238bc53d35aSJeremy Kerr 239672c8852SJeremy Kerr if (len > tx_size - 4) { 240672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 241672c8852SJeremy Kerr return; 242672c8852SJeremy Kerr } 243672c8852SJeremy Kerr 244df15f7e9SJeremy Kerr if (len > astlpc->binding.pkt_size) { 245672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 246672c8852SJeremy Kerr return; 247672c8852SJeremy Kerr } 248672c8852SJeremy Kerr 249df15f7e9SJeremy Kerr pkt = mctp_pktbuf_alloc(&astlpc->binding, len); 250672c8852SJeremy Kerr if (!pkt) 251672c8852SJeremy Kerr goto out_complete; 252672c8852SJeremy Kerr 253bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 254bc53d35aSJeremy Kerr memcpy(mctp_pktbuf_hdr(pkt), 255bc53d35aSJeremy Kerr astlpc->lpc_map + tx_offset + 4, len); 256bc53d35aSJeremy Kerr } else { 257bc53d35aSJeremy Kerr astlpc->ops.lpc_read(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 258bc53d35aSJeremy Kerr tx_offset + 4, len); 259bc53d35aSJeremy Kerr } 260672c8852SJeremy Kerr 261672c8852SJeremy Kerr mctp_bus_rx(&astlpc->binding, pkt); 262672c8852SJeremy Kerr 263672c8852SJeremy Kerr out_complete: 264672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x2); 265672c8852SJeremy Kerr } 266672c8852SJeremy Kerr 267672c8852SJeremy Kerr static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc) 268672c8852SJeremy Kerr { 269672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 270672c8852SJeremy Kerr } 271672c8852SJeremy Kerr 272672c8852SJeremy Kerr int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc) 273672c8852SJeremy Kerr { 274bc53d35aSJeremy Kerr uint8_t status, data; 275672c8852SJeremy Kerr int rc; 276672c8852SJeremy Kerr 277bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 278bc53d35aSJeremy Kerr &status); 279bc53d35aSJeremy Kerr if (rc) { 280672c8852SJeremy Kerr mctp_prwarn("KCS read error"); 281672c8852SJeremy Kerr return -1; 282672c8852SJeremy Kerr } 283672c8852SJeremy Kerr 284*edfe383fSAndrew Jeffery mctp_prdebug("%s: status: 0x%hhx", __func__, status); 285*edfe383fSAndrew Jeffery 286bc53d35aSJeremy Kerr if (!(status & KCS_STATUS_IBF)) 287672c8852SJeremy Kerr return 0; 288672c8852SJeremy Kerr 289bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 290bc53d35aSJeremy Kerr &data); 291bc53d35aSJeremy Kerr if (rc) { 292bc53d35aSJeremy Kerr mctp_prwarn("KCS data read error"); 293bc53d35aSJeremy Kerr return -1; 294bc53d35aSJeremy Kerr } 295bc53d35aSJeremy Kerr 296*edfe383fSAndrew Jeffery mctp_prdebug("%s: data: 0x%hhx", __func__, data); 297*edfe383fSAndrew Jeffery 298672c8852SJeremy Kerr switch (data) { 299672c8852SJeremy Kerr case 0x0: 300672c8852SJeremy Kerr mctp_astlpc_init_channel(astlpc); 301672c8852SJeremy Kerr break; 302672c8852SJeremy Kerr case 0x1: 303672c8852SJeremy Kerr mctp_astlpc_rx_start(astlpc); 304672c8852SJeremy Kerr break; 305672c8852SJeremy Kerr case 0x2: 306672c8852SJeremy Kerr mctp_astlpc_tx_complete(astlpc); 307672c8852SJeremy Kerr break; 3081a4b55acSJeremy Kerr case 0xff: 3091a4b55acSJeremy Kerr /* reserved value for dummy data writes; do nothing */ 3101a4b55acSJeremy Kerr break; 311672c8852SJeremy Kerr default: 312672c8852SJeremy Kerr mctp_prwarn("unknown message 0x%x", data); 313672c8852SJeremy Kerr } 314672c8852SJeremy Kerr return 0; 315672c8852SJeremy Kerr } 316672c8852SJeremy Kerr 317672c8852SJeremy Kerr static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc) 318672c8852SJeremy Kerr { 319bc53d35aSJeremy Kerr struct mctp_lpcmap_hdr *hdr; 320672c8852SJeremy Kerr uint8_t status; 321672c8852SJeremy Kerr int rc; 322672c8852SJeremy Kerr 323bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) 324bc53d35aSJeremy Kerr hdr = astlpc->lpc_hdr; 325bc53d35aSJeremy Kerr else 326bc53d35aSJeremy Kerr hdr = astlpc->priv_hdr; 327672c8852SJeremy Kerr 328bc53d35aSJeremy Kerr hdr->magic = htobe32(MCTP_MAGIC); 329bc53d35aSJeremy Kerr hdr->bmc_ver_min = htobe16(BMC_VER_MIN); 330bc53d35aSJeremy Kerr hdr->bmc_ver_cur = htobe16(BMC_VER_CUR); 331bc53d35aSJeremy Kerr 332bc53d35aSJeremy Kerr hdr->rx_offset = htobe32(rx_offset); 333bc53d35aSJeremy Kerr hdr->rx_size = htobe32(rx_size); 334bc53d35aSJeremy Kerr hdr->tx_offset = htobe32(tx_offset); 335bc53d35aSJeremy Kerr hdr->tx_size = htobe32(tx_size); 336bc53d35aSJeremy Kerr 337bc53d35aSJeremy Kerr if (!lpc_direct(astlpc)) 338bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, hdr, 0, sizeof(*hdr)); 339672c8852SJeremy Kerr 340672c8852SJeremy Kerr /* set status indicating that the BMC is now active */ 341672c8852SJeremy Kerr status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF; 342bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, 343bc53d35aSJeremy Kerr MCTP_ASTLPC_KCS_REG_STATUS, status); 344bc53d35aSJeremy Kerr if (rc) { 345672c8852SJeremy Kerr mctp_prwarn("KCS write failed"); 346672c8852SJeremy Kerr } 347672c8852SJeremy Kerr 348672c8852SJeremy Kerr return rc; 349672c8852SJeremy Kerr } 350672c8852SJeremy Kerr 3518081bebaSJeremy Kerr static int mctp_binding_astlpc_start(struct mctp_binding *b) 3528081bebaSJeremy Kerr { 3538081bebaSJeremy Kerr struct mctp_binding_astlpc *astlpc = container_of(b, 3548081bebaSJeremy Kerr struct mctp_binding_astlpc, binding); 3558081bebaSJeremy Kerr 3568081bebaSJeremy Kerr return mctp_astlpc_init_bmc(astlpc); 3578081bebaSJeremy Kerr } 3588081bebaSJeremy Kerr 359bc53d35aSJeremy Kerr /* allocate and basic initialisation */ 360bc53d35aSJeremy Kerr static struct mctp_binding_astlpc *__mctp_astlpc_init(void) 361bc53d35aSJeremy Kerr { 362bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc; 363bc53d35aSJeremy Kerr 364bc53d35aSJeremy Kerr astlpc = __mctp_alloc(sizeof(*astlpc)); 365bc53d35aSJeremy Kerr memset(astlpc, 0, sizeof(*astlpc)); 366bc53d35aSJeremy Kerr astlpc->binding.name = "astlpc"; 367bc53d35aSJeremy Kerr astlpc->binding.version = 1; 368bc53d35aSJeremy Kerr astlpc->binding.tx = mctp_binding_astlpc_tx; 3698081bebaSJeremy Kerr astlpc->binding.start = mctp_binding_astlpc_start; 37073c268e4SAndrew Jeffery astlpc->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU); 371bc53d35aSJeremy Kerr astlpc->binding.pkt_pad = 0; 372bc53d35aSJeremy Kerr astlpc->lpc_map = NULL; 373bc53d35aSJeremy Kerr 374bc53d35aSJeremy Kerr return astlpc; 375bc53d35aSJeremy Kerr } 376bc53d35aSJeremy Kerr 3773b36d17cSJeremy Kerr struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b) 3783b36d17cSJeremy Kerr { 3793b36d17cSJeremy Kerr return &b->binding; 3803b36d17cSJeremy Kerr } 3813b36d17cSJeremy Kerr 382bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_ops( 383a0452495SAndrew Jeffery const struct mctp_binding_astlpc_ops *ops, 384bc53d35aSJeremy Kerr void *ops_data, void *lpc_map) 385bc53d35aSJeremy Kerr { 386bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc; 387bc53d35aSJeremy Kerr 388bc53d35aSJeremy Kerr astlpc = __mctp_astlpc_init(); 389bc53d35aSJeremy Kerr if (!astlpc) 390bc53d35aSJeremy Kerr return NULL; 391bc53d35aSJeremy Kerr 392bc53d35aSJeremy Kerr memcpy(&astlpc->ops, ops, sizeof(astlpc->ops)); 393bc53d35aSJeremy Kerr astlpc->ops_data = ops_data; 394bc53d35aSJeremy Kerr astlpc->lpc_map = lpc_map; 395bc53d35aSJeremy Kerr 396bc53d35aSJeremy Kerr /* In indirect mode, we keep a separate buffer of header data. 397bc53d35aSJeremy Kerr * We need to sync this through the lpc_read/lpc_write ops. 398bc53d35aSJeremy Kerr */ 399bc53d35aSJeremy Kerr if (!astlpc->lpc_map) 400bc53d35aSJeremy Kerr astlpc->priv_hdr = __mctp_alloc(sizeof(*astlpc->priv_hdr)); 401bc53d35aSJeremy Kerr 402bc53d35aSJeremy Kerr return astlpc; 403bc53d35aSJeremy Kerr } 404bc53d35aSJeremy Kerr 405b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO 406bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc) 407672c8852SJeremy Kerr { 408672c8852SJeremy Kerr struct aspeed_lpc_ctrl_mapping map = { 409672c8852SJeremy Kerr .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY, 410672c8852SJeremy Kerr .window_id = 0, /* There's only one */ 411672c8852SJeremy Kerr .flags = 0, 412672c8852SJeremy Kerr .addr = 0, 413672c8852SJeremy Kerr .offset = 0, 414672c8852SJeremy Kerr .size = 0 415672c8852SJeremy Kerr }; 416672c8852SJeremy Kerr int fd, rc; 417672c8852SJeremy Kerr 418672c8852SJeremy Kerr fd = open(lpc_path, O_RDWR | O_SYNC); 419672c8852SJeremy Kerr if (fd < 0) { 420672c8852SJeremy Kerr mctp_prwarn("LPC open (%s) failed", lpc_path); 421672c8852SJeremy Kerr return -1; 422672c8852SJeremy Kerr } 423672c8852SJeremy Kerr 424672c8852SJeremy Kerr rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map); 425672c8852SJeremy Kerr if (rc) { 426672c8852SJeremy Kerr mctp_prwarn("LPC GET_SIZE failed"); 427672c8852SJeremy Kerr close(fd); 428672c8852SJeremy Kerr return -1; 429672c8852SJeremy Kerr } 430672c8852SJeremy Kerr 431672c8852SJeremy Kerr astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE, 432672c8852SJeremy Kerr MAP_SHARED, fd, 0); 433672c8852SJeremy Kerr if (astlpc->lpc_map_base == MAP_FAILED) { 434672c8852SJeremy Kerr mctp_prwarn("LPC mmap failed"); 435672c8852SJeremy Kerr rc = -1; 436672c8852SJeremy Kerr } else { 437672c8852SJeremy Kerr astlpc->lpc_map = astlpc->lpc_map_base + 438672c8852SJeremy Kerr map.size - LPC_WIN_SIZE; 439672c8852SJeremy Kerr } 440672c8852SJeremy Kerr 441672c8852SJeremy Kerr close(fd); 442672c8852SJeremy Kerr 443672c8852SJeremy Kerr return rc; 444672c8852SJeremy Kerr } 445672c8852SJeremy Kerr 446bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc) 447672c8852SJeremy Kerr { 448672c8852SJeremy Kerr astlpc->kcs_fd = open(kcs_path, O_RDWR); 449672c8852SJeremy Kerr if (astlpc->kcs_fd < 0) 450672c8852SJeremy Kerr return -1; 451672c8852SJeremy Kerr 452672c8852SJeremy Kerr return 0; 453672c8852SJeremy Kerr } 454672c8852SJeremy Kerr 455bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_read(void *arg, 456bc53d35aSJeremy Kerr enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val) 457bc53d35aSJeremy Kerr { 458bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc = arg; 459bc53d35aSJeremy Kerr off_t offset = reg; 460bc53d35aSJeremy Kerr int rc; 461bc53d35aSJeremy Kerr 462bc53d35aSJeremy Kerr rc = pread(astlpc->kcs_fd, val, 1, offset); 463bc53d35aSJeremy Kerr 464bc53d35aSJeremy Kerr return rc == 1 ? 0 : -1; 465bc53d35aSJeremy Kerr } 466bc53d35aSJeremy Kerr 467bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_write(void *arg, 468bc53d35aSJeremy Kerr enum mctp_binding_astlpc_kcs_reg reg, uint8_t val) 469bc53d35aSJeremy Kerr { 470bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc = arg; 471bc53d35aSJeremy Kerr off_t offset = reg; 472bc53d35aSJeremy Kerr int rc; 473bc53d35aSJeremy Kerr 474bc53d35aSJeremy Kerr rc = pwrite(astlpc->kcs_fd, &val, 1, offset); 475bc53d35aSJeremy Kerr 476bc53d35aSJeremy Kerr return rc == 1 ? 0 : -1; 477bc53d35aSJeremy Kerr } 478bc53d35aSJeremy Kerr 479bc53d35aSJeremy Kerr int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc) 480bc53d35aSJeremy Kerr { 481bc53d35aSJeremy Kerr return astlpc->kcs_fd; 482bc53d35aSJeremy Kerr } 483bc53d35aSJeremy Kerr 484bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void) 485672c8852SJeremy Kerr { 486672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc; 487672c8852SJeremy Kerr int rc; 488672c8852SJeremy Kerr 489bc53d35aSJeremy Kerr astlpc = __mctp_astlpc_init(); 490bc53d35aSJeremy Kerr if (!astlpc) 491bc53d35aSJeremy Kerr return NULL; 492672c8852SJeremy Kerr 493bc53d35aSJeremy Kerr /* Set internal operations for kcs. We use direct accesses to the lpc 494bc53d35aSJeremy Kerr * map area */ 495bc53d35aSJeremy Kerr astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read; 496bc53d35aSJeremy Kerr astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write; 497bc53d35aSJeremy Kerr astlpc->ops_data = astlpc; 498bc53d35aSJeremy Kerr 499bc53d35aSJeremy Kerr rc = mctp_astlpc_init_fileio_lpc(astlpc); 500672c8852SJeremy Kerr if (rc) { 501672c8852SJeremy Kerr free(astlpc); 502672c8852SJeremy Kerr return NULL; 503672c8852SJeremy Kerr } 504672c8852SJeremy Kerr 505bc53d35aSJeremy Kerr rc = mctp_astlpc_init_fileio_kcs(astlpc); 506672c8852SJeremy Kerr if (rc) { 507672c8852SJeremy Kerr free(astlpc); 508672c8852SJeremy Kerr return NULL; 509672c8852SJeremy Kerr } 510672c8852SJeremy Kerr 511672c8852SJeremy Kerr return astlpc; 512672c8852SJeremy Kerr } 51392a10a6bSJeremy Kerr #else 51492a10a6bSJeremy Kerr struct mctp_binding_astlpc * __attribute__((const)) 51592a10a6bSJeremy Kerr mctp_astlpc_init_fileio(void) 51692a10a6bSJeremy Kerr { 51759c6a5c9SAndrew Jeffery warnx("Missing support for file IO"); 51892a10a6bSJeremy Kerr return NULL; 51992a10a6bSJeremy Kerr } 520672c8852SJeremy Kerr 52192a10a6bSJeremy Kerr int __attribute__((const)) mctp_astlpc_get_fd( 52292a10a6bSJeremy Kerr struct mctp_binding_astlpc *astlpc __attribute__((unused))) 52392a10a6bSJeremy Kerr { 52459c6a5c9SAndrew Jeffery warnx("Missing support for file IO"); 52592a10a6bSJeremy Kerr return -1; 52692a10a6bSJeremy Kerr } 52792a10a6bSJeremy Kerr #endif 528