@@ -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
@@ -102,6 +102,13 @@ extern int64_t lpc_probe_write(enum OpalLPCAddressType addr_type, uint32_t addr,
extern int64_t lpc_probe_read(enum OpalLPCAddressType addr_type, uint32_t addr,
uint32_t *data, uint32_t sz);
+/*
+ * helpers for doing a bulk io to firmware space. These can be less restrictive
+ * since FW space generally acts like "normal memory."
+ */
+extern int64_t lpc_fw_read(uint32_t addr, void *buf, uint32_t sz);
+extern int64_t lpc_fw_write(uint32_t addr, const void *buf, uint32_t sz);
+
/* Mark LPC bus as used by console */
extern void lpc_used_by_console(void);
The normal LPC APIs are limited to 1/2/4 byte accesses. The existing SFC controller, mbox and HIOMAP flash drivers implement more or less the same copy loops to move data into and out of the LPC firmware space. Add a single generic helper that can replace the seperate implementations. Signed-off-by: Oliver O'Halloran <oohall@gmail.com> --- hw/lpc.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/lpc.h | 7 +++++ 2 files changed, 81 insertions(+)