diff options
Diffstat (limited to 'hw/lpc.c')
-rw-r--r-- | hw/lpc.c | 74 |
1 files changed, 74 insertions, 0 deletions
@@ -667,6 +667,80 @@ int64_t lpc_probe_read(enum OpalLPCAddressType addr_type, uint32_t addr, return __lpc_read_sanity(addr_type, addr, data, sz, true); } +int64_t lpc_fw_read(uint32_t off, void *buf, uint32_t len) +{ + int rc; + + prlog(PR_TRACE, "Reading 0x%08x bytes at FW offset 0x%08x\n", + len, off); + + while (len) { + uint32_t chunk; + uint32_t dat; + + /* XXX: make this read until it's aligned */ + if (len > 3 && !(off & 3)) { + rc = lpc_read(OPAL_LPC_FW, off, &dat, 4); + if (!rc) { + /* + * lpc_read swaps to CPU endian but it's not + * really a 32-bit value, so convert back. + */ + *(__be32 *)buf = cpu_to_be32(dat); + } + chunk = 4; + } else { + rc = lpc_read(OPAL_LPC_FW, off, &dat, 1); + if (!rc) + *(uint8_t *)buf = dat; + chunk = 1; + } + if (rc) { + prlog(PR_ERR, "lpc_read failure %d to FW 0x%08x\n", rc, off); + return rc; + } + len -= chunk; + off += chunk; + buf += chunk; + } + + return 0; +} + +int64_t lpc_fw_write(uint32_t off, const void *buf, uint32_t len) +{ + int rc; + + prlog(PR_TRACE, "Writing 0x%08x bytes at FW offset 0x%08x\n", + len, off); + + while (len) { + uint32_t chunk; + + if (len > 3 && !(off & 3)) { + /* endian swap: see lpc_window_write */ + uint32_t dat = be32_to_cpu(*(__be32 *)buf); + + rc = lpc_write(OPAL_LPC_FW, off, dat, 4); + chunk = 4; + } else { + uint8_t dat = *(uint8_t *)buf; + + rc = lpc_write(OPAL_LPC_FW, off, dat, 1); + chunk = 1; + } + if (rc) { + prlog(PR_ERR, "lpc_write failure %d to FW 0x%08x\n", rc, off); + return rc; + } + len -= chunk; + off += chunk; + buf += chunk; + } + + return 0; +} + /* * The "OPAL" variant add the emulation of 2 and 4 byte accesses using * byte accesses for IO and MEM space in order to be compatible with |