aboutsummaryrefslogtreecommitdiff
path: root/hw/lpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/lpc.c')
-rw-r--r--hw/lpc.c74
1 files changed, 74 insertions, 0 deletions
diff --git a/hw/lpc.c b/hw/lpc.c
index bf3ab1f..caaacc4 100644
--- a/hw/lpc.c
+++ b/hw/lpc.c
@@ -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