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> 6672c8852SJeremy Kerr #include <stdbool.h> 7672c8852SJeremy Kerr #include <stdlib.h> 8672c8852SJeremy Kerr #include <string.h> 9672c8852SJeremy Kerr 10672c8852SJeremy Kerr #define pr_fmt(x) "astlpc: " x 11672c8852SJeremy Kerr 1259c6a5c9SAndrew Jeffery #if HAVE_CONFIG_H 1359c6a5c9SAndrew Jeffery #include "config.h" 1459c6a5c9SAndrew Jeffery #endif 1559c6a5c9SAndrew Jeffery 16672c8852SJeremy Kerr #include "libmctp.h" 17672c8852SJeremy Kerr #include "libmctp-alloc.h" 18672c8852SJeremy Kerr #include "libmctp-log.h" 19672c8852SJeremy Kerr #include "libmctp-astlpc.h" 20672c8852SJeremy Kerr 21b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO 2292a10a6bSJeremy Kerr 23c6f676d1SJeremy Kerr #include <unistd.h> 2492a10a6bSJeremy Kerr #include <fcntl.h> 2592a10a6bSJeremy Kerr #include <sys/ioctl.h> 2692a10a6bSJeremy Kerr #include <sys/mman.h> 2792a10a6bSJeremy Kerr #include <linux/aspeed-lpc-ctrl.h> 2892a10a6bSJeremy Kerr 2992a10a6bSJeremy Kerr /* kernel interface */ 3092a10a6bSJeremy Kerr static const char *kcs_path = "/dev/mctp0"; 3192a10a6bSJeremy Kerr static const char *lpc_path = "/dev/aspeed-lpc-ctrl"; 3292a10a6bSJeremy Kerr 3392a10a6bSJeremy Kerr #endif 3492a10a6bSJeremy Kerr 35672c8852SJeremy Kerr struct mctp_binding_astlpc { 36672c8852SJeremy Kerr struct mctp_binding binding; 37bc53d35aSJeremy Kerr 38672c8852SJeremy Kerr union { 39672c8852SJeremy Kerr void *lpc_map; 40672c8852SJeremy Kerr struct mctp_lpcmap_hdr *lpc_hdr; 41672c8852SJeremy Kerr }; 42bc53d35aSJeremy Kerr 43bc53d35aSJeremy Kerr /* direct ops data */ 44bc53d35aSJeremy Kerr struct mctp_binding_astlpc_ops ops; 45bc53d35aSJeremy Kerr void *ops_data; 46bc53d35aSJeremy Kerr struct mctp_lpcmap_hdr *priv_hdr; 47bc53d35aSJeremy Kerr 48bc53d35aSJeremy Kerr /* fileio ops data */ 49bc53d35aSJeremy Kerr void *lpc_map_base; 50672c8852SJeremy Kerr int kcs_fd; 51672c8852SJeremy Kerr uint8_t kcs_status; 52672c8852SJeremy Kerr 53672c8852SJeremy Kerr bool running; 54672c8852SJeremy Kerr 55672c8852SJeremy Kerr /* temporary transmit buffer */ 56672c8852SJeremy Kerr uint8_t txbuf[256]; 57672c8852SJeremy Kerr }; 58672c8852SJeremy Kerr 59672c8852SJeremy Kerr #ifndef container_of 60672c8852SJeremy Kerr #define container_of(ptr, type, member) \ 61672c8852SJeremy Kerr (type *)((char *)(ptr) - (char *)&((type *)0)->member) 62672c8852SJeremy Kerr #endif 63672c8852SJeremy Kerr 64672c8852SJeremy Kerr #define binding_to_astlpc(b) \ 65672c8852SJeremy Kerr container_of(b, struct mctp_binding_astlpc, binding) 66672c8852SJeremy Kerr 67672c8852SJeremy Kerr #define MCTP_MAGIC 0x4d435450 68672c8852SJeremy Kerr #define BMC_VER_MIN 1 69672c8852SJeremy Kerr #define BMC_VER_CUR 1 70672c8852SJeremy Kerr 71672c8852SJeremy Kerr struct mctp_lpcmap_hdr { 72672c8852SJeremy Kerr uint32_t magic; 73672c8852SJeremy Kerr 74672c8852SJeremy Kerr uint16_t bmc_ver_min; 75672c8852SJeremy Kerr uint16_t bmc_ver_cur; 76672c8852SJeremy Kerr uint16_t host_ver_min; 77672c8852SJeremy Kerr uint16_t host_ver_cur; 78672c8852SJeremy Kerr uint16_t negotiated_ver; 79672c8852SJeremy Kerr uint16_t pad0; 80672c8852SJeremy Kerr 81672c8852SJeremy Kerr uint32_t rx_offset; 82672c8852SJeremy Kerr uint32_t rx_size; 83672c8852SJeremy Kerr uint32_t tx_offset; 84672c8852SJeremy Kerr uint32_t tx_size; 85672c8852SJeremy Kerr } __attribute__((packed)); 86672c8852SJeremy Kerr 87672c8852SJeremy Kerr /* layout of TX/RX areas */ 88672c8852SJeremy Kerr static const uint32_t rx_offset = 0x100; 89672c8852SJeremy Kerr static const uint32_t rx_size = 0x100; 90672c8852SJeremy Kerr static const uint32_t tx_offset = 0x200; 91672c8852SJeremy Kerr static const uint32_t tx_size = 0x100; 92672c8852SJeremy Kerr 93672c8852SJeremy Kerr #define LPC_WIN_SIZE (1 * 1024 * 1024) 94672c8852SJeremy Kerr 95672c8852SJeremy Kerr enum { 96672c8852SJeremy Kerr KCS_REG_DATA = 0, 97672c8852SJeremy Kerr KCS_REG_STATUS = 1, 98672c8852SJeremy Kerr }; 99672c8852SJeremy Kerr 100672c8852SJeremy Kerr #define KCS_STATUS_BMC_READY 0x80 101672c8852SJeremy Kerr #define KCS_STATUS_CHANNEL_ACTIVE 0x40 102672c8852SJeremy Kerr #define KCS_STATUS_IBF 0x02 103672c8852SJeremy Kerr #define KCS_STATUS_OBF 0x01 104672c8852SJeremy Kerr 105bc53d35aSJeremy Kerr static bool lpc_direct(struct mctp_binding_astlpc *astlpc) 106bc53d35aSJeremy Kerr { 107bc53d35aSJeremy Kerr return astlpc->lpc_map != NULL; 108bc53d35aSJeremy Kerr } 109bc53d35aSJeremy Kerr 110672c8852SJeremy Kerr static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc, 111672c8852SJeremy Kerr uint8_t status) 112672c8852SJeremy Kerr { 113bc53d35aSJeremy Kerr uint8_t data; 114672c8852SJeremy Kerr int rc; 115672c8852SJeremy Kerr 1161a4b55acSJeremy Kerr /* Since we're setting the status register, we want the other endpoint 1171a4b55acSJeremy Kerr * to be interrupted. However, some hardware may only raise a host-side 1181a4b55acSJeremy Kerr * interrupt on an ODR event. 1191a4b55acSJeremy Kerr * So, write a dummy value of 0xff to ODR, which will ensure that an 1201a4b55acSJeremy Kerr * interrupt is triggered, and can be ignored by the host. 1211a4b55acSJeremy Kerr */ 122bc53d35aSJeremy Kerr data = 0xff; 123bc53d35aSJeremy Kerr status |= KCS_STATUS_OBF; 1241a4b55acSJeremy Kerr 125bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 126bc53d35aSJeremy Kerr data); 127bc53d35aSJeremy Kerr if (rc) { 128bc53d35aSJeremy Kerr mctp_prwarn("KCS dummy data write failed"); 129bc53d35aSJeremy Kerr return -1; 130bc53d35aSJeremy Kerr } 131bc53d35aSJeremy Kerr 132bc53d35aSJeremy Kerr 133bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 134bc53d35aSJeremy Kerr status); 135bc53d35aSJeremy Kerr if (rc) { 136672c8852SJeremy Kerr mctp_prwarn("KCS status write failed"); 137672c8852SJeremy Kerr return -1; 138672c8852SJeremy Kerr } 139672c8852SJeremy Kerr return 0; 140672c8852SJeremy Kerr } 141672c8852SJeremy Kerr 142672c8852SJeremy Kerr static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc, 143672c8852SJeremy Kerr uint8_t data) 144672c8852SJeremy Kerr { 145672c8852SJeremy Kerr uint8_t status; 146672c8852SJeremy Kerr int rc; 147672c8852SJeremy Kerr 148672c8852SJeremy Kerr for (;;) { 149bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, 150bc53d35aSJeremy Kerr MCTP_ASTLPC_KCS_REG_STATUS, &status); 151*1b27fe87SAndrew Jeffery if (rc) { 152182204e3SAndrew Jeffery mctp_prwarn("KCS status read failed"); 153672c8852SJeremy Kerr return -1; 154672c8852SJeremy Kerr } 155672c8852SJeremy Kerr if (!(status & KCS_STATUS_OBF)) 156672c8852SJeremy Kerr break; 157672c8852SJeremy Kerr /* todo: timeout */ 158672c8852SJeremy Kerr } 159672c8852SJeremy Kerr 160bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 161bc53d35aSJeremy Kerr data); 162bc53d35aSJeremy Kerr if (rc) { 163672c8852SJeremy Kerr mctp_prwarn("KCS data write failed"); 164672c8852SJeremy Kerr return -1; 165672c8852SJeremy Kerr } 166672c8852SJeremy Kerr 167672c8852SJeremy Kerr return 0; 168672c8852SJeremy Kerr } 169672c8852SJeremy Kerr 170672c8852SJeremy Kerr static int mctp_binding_astlpc_tx(struct mctp_binding *b, 171672c8852SJeremy Kerr struct mctp_pktbuf *pkt) 172672c8852SJeremy Kerr { 173672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b); 174672c8852SJeremy Kerr uint32_t len; 175672c8852SJeremy Kerr 176672c8852SJeremy Kerr len = mctp_pktbuf_size(pkt); 177672c8852SJeremy Kerr if (len > rx_size - 4) { 178672c8852SJeremy Kerr mctp_prwarn("invalid TX len 0x%x", len); 179672c8852SJeremy Kerr return -1; 180672c8852SJeremy Kerr } 181672c8852SJeremy Kerr 182bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 183672c8852SJeremy Kerr *(uint32_t *)(astlpc->lpc_map + rx_offset) = htobe32(len); 184bc53d35aSJeremy Kerr memcpy(astlpc->lpc_map + rx_offset + 4, mctp_pktbuf_hdr(pkt), 185bc53d35aSJeremy Kerr len); 186bc53d35aSJeremy Kerr } else { 187bc53d35aSJeremy Kerr uint32_t tmp = htobe32(len); 188bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, &tmp, rx_offset, 189bc53d35aSJeremy Kerr sizeof(tmp)); 190bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 191bc53d35aSJeremy Kerr rx_offset + 4, len); 192bc53d35aSJeremy Kerr } 193672c8852SJeremy Kerr 194672c8852SJeremy Kerr mctp_binding_set_tx_enabled(b, false); 195672c8852SJeremy Kerr 196672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x1); 197672c8852SJeremy Kerr return 0; 198672c8852SJeremy Kerr } 199672c8852SJeremy Kerr 200672c8852SJeremy Kerr static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc) 201672c8852SJeremy Kerr { 202672c8852SJeremy Kerr /* todo: actual version negotiation */ 203bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 204672c8852SJeremy Kerr astlpc->lpc_hdr->negotiated_ver = htobe16(1); 205bc53d35aSJeremy Kerr } else { 206bc53d35aSJeremy Kerr uint16_t ver = htobe16(1); 207bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, &ver, 208bc53d35aSJeremy Kerr offsetof(struct mctp_lpcmap_hdr, 209bc53d35aSJeremy Kerr negotiated_ver), 210bc53d35aSJeremy Kerr sizeof(ver)); 211bc53d35aSJeremy Kerr } 212672c8852SJeremy Kerr mctp_astlpc_kcs_set_status(astlpc, 213672c8852SJeremy Kerr KCS_STATUS_BMC_READY | KCS_STATUS_CHANNEL_ACTIVE | 214672c8852SJeremy Kerr KCS_STATUS_OBF); 215672c8852SJeremy Kerr 216672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 217672c8852SJeremy Kerr } 218672c8852SJeremy Kerr 219672c8852SJeremy Kerr static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc) 220672c8852SJeremy Kerr { 221672c8852SJeremy Kerr struct mctp_pktbuf *pkt; 222672c8852SJeremy Kerr uint32_t len; 223672c8852SJeremy Kerr 224bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 225bc53d35aSJeremy Kerr len = *(uint32_t *)(astlpc->lpc_map + tx_offset); 226bc53d35aSJeremy Kerr } else { 227bc53d35aSJeremy Kerr astlpc->ops.lpc_read(astlpc->ops_data, &len, 228bc53d35aSJeremy Kerr tx_offset, sizeof(len)); 229bc53d35aSJeremy Kerr } 230bc53d35aSJeremy Kerr len = be32toh(len); 231bc53d35aSJeremy Kerr 232672c8852SJeremy Kerr if (len > tx_size - 4) { 233672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 234672c8852SJeremy Kerr return; 235672c8852SJeremy Kerr } 236672c8852SJeremy Kerr 237df15f7e9SJeremy Kerr if (len > astlpc->binding.pkt_size) { 238672c8852SJeremy Kerr mctp_prwarn("invalid RX len 0x%x", len); 239672c8852SJeremy Kerr return; 240672c8852SJeremy Kerr } 241672c8852SJeremy Kerr 242df15f7e9SJeremy Kerr pkt = mctp_pktbuf_alloc(&astlpc->binding, len); 243672c8852SJeremy Kerr if (!pkt) 244672c8852SJeremy Kerr goto out_complete; 245672c8852SJeremy Kerr 246bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) { 247bc53d35aSJeremy Kerr memcpy(mctp_pktbuf_hdr(pkt), 248bc53d35aSJeremy Kerr astlpc->lpc_map + tx_offset + 4, len); 249bc53d35aSJeremy Kerr } else { 250bc53d35aSJeremy Kerr astlpc->ops.lpc_read(astlpc->ops_data, mctp_pktbuf_hdr(pkt), 251bc53d35aSJeremy Kerr tx_offset + 4, len); 252bc53d35aSJeremy Kerr } 253672c8852SJeremy Kerr 254672c8852SJeremy Kerr mctp_bus_rx(&astlpc->binding, pkt); 255672c8852SJeremy Kerr 256672c8852SJeremy Kerr out_complete: 257672c8852SJeremy Kerr mctp_astlpc_kcs_send(astlpc, 0x2); 258672c8852SJeremy Kerr } 259672c8852SJeremy Kerr 260672c8852SJeremy Kerr static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc) 261672c8852SJeremy Kerr { 262672c8852SJeremy Kerr mctp_binding_set_tx_enabled(&astlpc->binding, true); 263672c8852SJeremy Kerr } 264672c8852SJeremy Kerr 265672c8852SJeremy Kerr int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc) 266672c8852SJeremy Kerr { 267bc53d35aSJeremy Kerr uint8_t status, data; 268672c8852SJeremy Kerr int rc; 269672c8852SJeremy Kerr 270bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_STATUS, 271bc53d35aSJeremy Kerr &status); 272bc53d35aSJeremy Kerr if (rc) { 273672c8852SJeremy Kerr mctp_prwarn("KCS read error"); 274672c8852SJeremy Kerr return -1; 275672c8852SJeremy Kerr } 276672c8852SJeremy Kerr 277bc53d35aSJeremy Kerr if (!(status & KCS_STATUS_IBF)) 278672c8852SJeremy Kerr return 0; 279672c8852SJeremy Kerr 280bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_read(astlpc->ops_data, MCTP_ASTLPC_KCS_REG_DATA, 281bc53d35aSJeremy Kerr &data); 282bc53d35aSJeremy Kerr if (rc) { 283bc53d35aSJeremy Kerr mctp_prwarn("KCS data read error"); 284bc53d35aSJeremy Kerr return -1; 285bc53d35aSJeremy Kerr } 286bc53d35aSJeremy Kerr 287672c8852SJeremy Kerr switch (data) { 288672c8852SJeremy Kerr case 0x0: 289672c8852SJeremy Kerr mctp_astlpc_init_channel(astlpc); 290672c8852SJeremy Kerr break; 291672c8852SJeremy Kerr case 0x1: 292672c8852SJeremy Kerr mctp_astlpc_rx_start(astlpc); 293672c8852SJeremy Kerr break; 294672c8852SJeremy Kerr case 0x2: 295672c8852SJeremy Kerr mctp_astlpc_tx_complete(astlpc); 296672c8852SJeremy Kerr break; 2971a4b55acSJeremy Kerr case 0xff: 2981a4b55acSJeremy Kerr /* reserved value for dummy data writes; do nothing */ 2991a4b55acSJeremy Kerr break; 300672c8852SJeremy Kerr default: 301672c8852SJeremy Kerr mctp_prwarn("unknown message 0x%x", data); 302672c8852SJeremy Kerr } 303672c8852SJeremy Kerr return 0; 304672c8852SJeremy Kerr } 305672c8852SJeremy Kerr 306672c8852SJeremy Kerr static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc) 307672c8852SJeremy Kerr { 308bc53d35aSJeremy Kerr struct mctp_lpcmap_hdr *hdr; 309672c8852SJeremy Kerr uint8_t status; 310672c8852SJeremy Kerr int rc; 311672c8852SJeremy Kerr 312bc53d35aSJeremy Kerr if (lpc_direct(astlpc)) 313bc53d35aSJeremy Kerr hdr = astlpc->lpc_hdr; 314bc53d35aSJeremy Kerr else 315bc53d35aSJeremy Kerr hdr = astlpc->priv_hdr; 316672c8852SJeremy Kerr 317bc53d35aSJeremy Kerr hdr->magic = htobe32(MCTP_MAGIC); 318bc53d35aSJeremy Kerr hdr->bmc_ver_min = htobe16(BMC_VER_MIN); 319bc53d35aSJeremy Kerr hdr->bmc_ver_cur = htobe16(BMC_VER_CUR); 320bc53d35aSJeremy Kerr 321bc53d35aSJeremy Kerr hdr->rx_offset = htobe32(rx_offset); 322bc53d35aSJeremy Kerr hdr->rx_size = htobe32(rx_size); 323bc53d35aSJeremy Kerr hdr->tx_offset = htobe32(tx_offset); 324bc53d35aSJeremy Kerr hdr->tx_size = htobe32(tx_size); 325bc53d35aSJeremy Kerr 326bc53d35aSJeremy Kerr if (!lpc_direct(astlpc)) 327bc53d35aSJeremy Kerr astlpc->ops.lpc_write(astlpc->ops_data, hdr, 0, sizeof(*hdr)); 328672c8852SJeremy Kerr 329672c8852SJeremy Kerr /* set status indicating that the BMC is now active */ 330672c8852SJeremy Kerr status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF; 331bc53d35aSJeremy Kerr rc = astlpc->ops.kcs_write(astlpc->ops_data, 332bc53d35aSJeremy Kerr MCTP_ASTLPC_KCS_REG_STATUS, status); 333bc53d35aSJeremy Kerr if (rc) { 334672c8852SJeremy Kerr mctp_prwarn("KCS write failed"); 335672c8852SJeremy Kerr } 336672c8852SJeremy Kerr 337672c8852SJeremy Kerr return rc; 338672c8852SJeremy Kerr } 339672c8852SJeremy Kerr 3408081bebaSJeremy Kerr static int mctp_binding_astlpc_start(struct mctp_binding *b) 3418081bebaSJeremy Kerr { 3428081bebaSJeremy Kerr struct mctp_binding_astlpc *astlpc = container_of(b, 3438081bebaSJeremy Kerr struct mctp_binding_astlpc, binding); 3448081bebaSJeremy Kerr 3458081bebaSJeremy Kerr return mctp_astlpc_init_bmc(astlpc); 3468081bebaSJeremy Kerr } 3478081bebaSJeremy Kerr 348bc53d35aSJeremy Kerr /* allocate and basic initialisation */ 349bc53d35aSJeremy Kerr static struct mctp_binding_astlpc *__mctp_astlpc_init(void) 350bc53d35aSJeremy Kerr { 351bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc; 352bc53d35aSJeremy Kerr 353bc53d35aSJeremy Kerr astlpc = __mctp_alloc(sizeof(*astlpc)); 354bc53d35aSJeremy Kerr memset(astlpc, 0, sizeof(*astlpc)); 355bc53d35aSJeremy Kerr astlpc->binding.name = "astlpc"; 356bc53d35aSJeremy Kerr astlpc->binding.version = 1; 357bc53d35aSJeremy Kerr astlpc->binding.tx = mctp_binding_astlpc_tx; 3588081bebaSJeremy Kerr astlpc->binding.start = mctp_binding_astlpc_start; 359bc53d35aSJeremy Kerr astlpc->binding.pkt_size = MCTP_BMTU; 360bc53d35aSJeremy Kerr astlpc->binding.pkt_pad = 0; 361bc53d35aSJeremy Kerr astlpc->lpc_map = NULL; 362bc53d35aSJeremy Kerr 363bc53d35aSJeremy Kerr return astlpc; 364bc53d35aSJeremy Kerr } 365bc53d35aSJeremy Kerr 3663b36d17cSJeremy Kerr struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b) 3673b36d17cSJeremy Kerr { 3683b36d17cSJeremy Kerr return &b->binding; 3693b36d17cSJeremy Kerr } 3703b36d17cSJeremy Kerr 371bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_ops( 372bc53d35aSJeremy Kerr struct mctp_binding_astlpc_ops *ops, 373bc53d35aSJeremy Kerr void *ops_data, void *lpc_map) 374bc53d35aSJeremy Kerr { 375bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc; 376bc53d35aSJeremy Kerr 377bc53d35aSJeremy Kerr astlpc = __mctp_astlpc_init(); 378bc53d35aSJeremy Kerr if (!astlpc) 379bc53d35aSJeremy Kerr return NULL; 380bc53d35aSJeremy Kerr 381bc53d35aSJeremy Kerr memcpy(&astlpc->ops, ops, sizeof(astlpc->ops)); 382bc53d35aSJeremy Kerr astlpc->ops_data = ops_data; 383bc53d35aSJeremy Kerr astlpc->lpc_map = lpc_map; 384bc53d35aSJeremy Kerr 385bc53d35aSJeremy Kerr /* In indirect mode, we keep a separate buffer of header data. 386bc53d35aSJeremy Kerr * We need to sync this through the lpc_read/lpc_write ops. 387bc53d35aSJeremy Kerr */ 388bc53d35aSJeremy Kerr if (!astlpc->lpc_map) 389bc53d35aSJeremy Kerr astlpc->priv_hdr = __mctp_alloc(sizeof(*astlpc->priv_hdr)); 390bc53d35aSJeremy Kerr 391bc53d35aSJeremy Kerr return astlpc; 392bc53d35aSJeremy Kerr } 393bc53d35aSJeremy Kerr 394b214c643SJeremy Kerr #ifdef MCTP_HAVE_FILEIO 395bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc) 396672c8852SJeremy Kerr { 397672c8852SJeremy Kerr struct aspeed_lpc_ctrl_mapping map = { 398672c8852SJeremy Kerr .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY, 399672c8852SJeremy Kerr .window_id = 0, /* There's only one */ 400672c8852SJeremy Kerr .flags = 0, 401672c8852SJeremy Kerr .addr = 0, 402672c8852SJeremy Kerr .offset = 0, 403672c8852SJeremy Kerr .size = 0 404672c8852SJeremy Kerr }; 405672c8852SJeremy Kerr int fd, rc; 406672c8852SJeremy Kerr 407672c8852SJeremy Kerr fd = open(lpc_path, O_RDWR | O_SYNC); 408672c8852SJeremy Kerr if (fd < 0) { 409672c8852SJeremy Kerr mctp_prwarn("LPC open (%s) failed", lpc_path); 410672c8852SJeremy Kerr return -1; 411672c8852SJeremy Kerr } 412672c8852SJeremy Kerr 413672c8852SJeremy Kerr rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map); 414672c8852SJeremy Kerr if (rc) { 415672c8852SJeremy Kerr mctp_prwarn("LPC GET_SIZE failed"); 416672c8852SJeremy Kerr close(fd); 417672c8852SJeremy Kerr return -1; 418672c8852SJeremy Kerr } 419672c8852SJeremy Kerr 420672c8852SJeremy Kerr astlpc->lpc_map_base = mmap(NULL, map.size, PROT_READ | PROT_WRITE, 421672c8852SJeremy Kerr MAP_SHARED, fd, 0); 422672c8852SJeremy Kerr if (astlpc->lpc_map_base == MAP_FAILED) { 423672c8852SJeremy Kerr mctp_prwarn("LPC mmap failed"); 424672c8852SJeremy Kerr rc = -1; 425672c8852SJeremy Kerr } else { 426672c8852SJeremy Kerr astlpc->lpc_map = astlpc->lpc_map_base + 427672c8852SJeremy Kerr map.size - LPC_WIN_SIZE; 428672c8852SJeremy Kerr } 429672c8852SJeremy Kerr 430672c8852SJeremy Kerr close(fd); 431672c8852SJeremy Kerr 432672c8852SJeremy Kerr return rc; 433672c8852SJeremy Kerr } 434672c8852SJeremy Kerr 435bc53d35aSJeremy Kerr static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc) 436672c8852SJeremy Kerr { 437672c8852SJeremy Kerr astlpc->kcs_fd = open(kcs_path, O_RDWR); 438672c8852SJeremy Kerr if (astlpc->kcs_fd < 0) 439672c8852SJeremy Kerr return -1; 440672c8852SJeremy Kerr 441672c8852SJeremy Kerr return 0; 442672c8852SJeremy Kerr } 443672c8852SJeremy Kerr 444bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_read(void *arg, 445bc53d35aSJeremy Kerr enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val) 446bc53d35aSJeremy Kerr { 447bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc = arg; 448bc53d35aSJeremy Kerr off_t offset = reg; 449bc53d35aSJeremy Kerr int rc; 450bc53d35aSJeremy Kerr 451bc53d35aSJeremy Kerr rc = pread(astlpc->kcs_fd, val, 1, offset); 452bc53d35aSJeremy Kerr 453bc53d35aSJeremy Kerr return rc == 1 ? 0 : -1; 454bc53d35aSJeremy Kerr } 455bc53d35aSJeremy Kerr 456bc53d35aSJeremy Kerr static int __mctp_astlpc_fileio_kcs_write(void *arg, 457bc53d35aSJeremy Kerr enum mctp_binding_astlpc_kcs_reg reg, uint8_t val) 458bc53d35aSJeremy Kerr { 459bc53d35aSJeremy Kerr struct mctp_binding_astlpc *astlpc = arg; 460bc53d35aSJeremy Kerr off_t offset = reg; 461bc53d35aSJeremy Kerr int rc; 462bc53d35aSJeremy Kerr 463bc53d35aSJeremy Kerr rc = pwrite(astlpc->kcs_fd, &val, 1, offset); 464bc53d35aSJeremy Kerr 465bc53d35aSJeremy Kerr return rc == 1 ? 0 : -1; 466bc53d35aSJeremy Kerr } 467bc53d35aSJeremy Kerr 468bc53d35aSJeremy Kerr int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc) 469bc53d35aSJeremy Kerr { 470bc53d35aSJeremy Kerr return astlpc->kcs_fd; 471bc53d35aSJeremy Kerr } 472bc53d35aSJeremy Kerr 473bc53d35aSJeremy Kerr struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void) 474672c8852SJeremy Kerr { 475672c8852SJeremy Kerr struct mctp_binding_astlpc *astlpc; 476672c8852SJeremy Kerr int rc; 477672c8852SJeremy Kerr 478bc53d35aSJeremy Kerr astlpc = __mctp_astlpc_init(); 479bc53d35aSJeremy Kerr if (!astlpc) 480bc53d35aSJeremy Kerr return NULL; 481672c8852SJeremy Kerr 482bc53d35aSJeremy Kerr /* Set internal operations for kcs. We use direct accesses to the lpc 483bc53d35aSJeremy Kerr * map area */ 484bc53d35aSJeremy Kerr astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read; 485bc53d35aSJeremy Kerr astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write; 486bc53d35aSJeremy Kerr astlpc->ops_data = astlpc; 487bc53d35aSJeremy Kerr 488bc53d35aSJeremy Kerr rc = mctp_astlpc_init_fileio_lpc(astlpc); 489672c8852SJeremy Kerr if (rc) { 490672c8852SJeremy Kerr free(astlpc); 491672c8852SJeremy Kerr return NULL; 492672c8852SJeremy Kerr } 493672c8852SJeremy Kerr 494bc53d35aSJeremy Kerr rc = mctp_astlpc_init_fileio_kcs(astlpc); 495672c8852SJeremy Kerr if (rc) { 496672c8852SJeremy Kerr free(astlpc); 497672c8852SJeremy Kerr return NULL; 498672c8852SJeremy Kerr } 499672c8852SJeremy Kerr 500672c8852SJeremy Kerr return astlpc; 501672c8852SJeremy Kerr } 50292a10a6bSJeremy Kerr #else 50392a10a6bSJeremy Kerr struct mctp_binding_astlpc * __attribute__((const)) 50492a10a6bSJeremy Kerr mctp_astlpc_init_fileio(void) 50592a10a6bSJeremy Kerr { 50659c6a5c9SAndrew Jeffery warnx("Missing support for file IO"); 50792a10a6bSJeremy Kerr return NULL; 50892a10a6bSJeremy Kerr } 509672c8852SJeremy Kerr 51092a10a6bSJeremy Kerr int __attribute__((const)) mctp_astlpc_get_fd( 51192a10a6bSJeremy Kerr struct mctp_binding_astlpc *astlpc __attribute__((unused))) 51292a10a6bSJeremy Kerr { 51359c6a5c9SAndrew Jeffery warnx("Missing support for file IO"); 51492a10a6bSJeremy Kerr return -1; 51592a10a6bSJeremy Kerr } 51692a10a6bSJeremy Kerr #endif 517