aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/xscom.c75
-rw-r--r--include/xscom.h12
2 files changed, 87 insertions, 0 deletions
diff --git a/hw/xscom.c b/hw/xscom.c
index 88e22da..32c813e 100644
--- a/hw/xscom.c
+++ b/hw/xscom.c
@@ -580,11 +580,75 @@ void _xscom_unlock(void)
unlock(&xscom_lock);
}
+/* sorted by the scom controller's partid */
+static LIST_HEAD(scom_list);
+
+int64_t scom_register(struct scom_controller *new)
+{
+ struct scom_controller *cur;
+
+ list_for_each(&scom_list, cur, link) {
+ if (cur->part_id == new->part_id) {
+ prerror("Attempted to add duplicate scom, partid %x\n",
+ new->part_id);
+ return OPAL_BUSY;
+ }
+
+ if (cur->part_id > new->part_id) {
+ list_add_before(&scom_list, &new->link, &cur->link);
+ return 0;
+ }
+ }
+
+ /* if we never find a larger partid then this is the largest */
+ list_add_tail(&scom_list, &new->link);
+
+ return 0;
+}
+
+static struct scom_controller *scom_find(uint32_t partid)
+{
+ struct scom_controller *cur;
+
+ list_for_each(&scom_list, cur, link)
+ if (partid == cur->part_id)
+ return cur;
+
+ return NULL;
+}
+
+static int64_t scom_read(struct scom_controller *scom, uint32_t partid,
+ uint64_t pcbaddr, uint64_t *val)
+{
+ int64_t rc = scom->read(scom, partid, pcbaddr, val);
+
+ if (rc) {
+ prerror("%s: to %x off: %llx rc = %lld\n",
+ __func__, partid, pcbaddr, rc);
+ }
+
+ return rc;
+}
+
+static int64_t scom_write(struct scom_controller *scom, uint32_t partid,
+ uint64_t pcbaddr, uint64_t val)
+{
+ int64_t rc = scom->write(scom, partid, pcbaddr, val);
+
+ if (rc) {
+ prerror("%s: to %x off: %llx rc = %lld\n",
+ __func__, partid, pcbaddr, rc);
+ }
+
+ return rc;
+}
+
/*
* External API
*/
int _xscom_read(uint32_t partid, uint64_t pcb_addr, uint64_t *val, bool take_lock)
{
+ struct scom_controller *scom;
uint32_t gcid;
int rc;
@@ -611,6 +675,11 @@ int _xscom_read(uint32_t partid, uint64_t pcb_addr, uint64_t *val, bool take_loc
return OPAL_UNSUPPORTED;
break;
default:
+ /* is it one of our hacks? */
+ scom = scom_find(partid);
+ if (scom)
+ return scom_read(scom, partid, pcb_addr, val);
+
/**
* @fwts-label XSCOMReadInvalidPartID
* @fwts-advice xscom_read was called with an invalid partid.
@@ -652,6 +721,7 @@ opal_call(OPAL_XSCOM_READ, opal_xscom_read, 3);
int _xscom_write(uint32_t partid, uint64_t pcb_addr, uint64_t val, bool take_lock)
{
+ struct scom_controller *scom;
uint32_t gcid;
int rc;
@@ -666,6 +736,11 @@ int _xscom_write(uint32_t partid, uint64_t pcb_addr, uint64_t val, bool take_loc
gcid = xscom_decode_chiplet(partid, &pcb_addr);
break;
default:
+ /* is it one of our hacks? */
+ scom = scom_find(partid);
+ if (scom)
+ return scom_write(scom, partid, pcb_addr, val);
+
/**
* @fwts-label XSCOMWriteInvalidPartID
* @fwts-advice xscom_write was called with an invalid partid.
diff --git a/include/xscom.h b/include/xscom.h
index 110aa8d..bd8bb89 100644
--- a/include/xscom.h
+++ b/include/xscom.h
@@ -197,4 +197,16 @@ extern bool xscom_ok(void);
extern int64_t xscom_read_cfam_chipid(uint32_t partid, uint32_t *chip_id);
extern int64_t xscom_trigger_xstop(void);
+
+struct scom_controller {
+ uint32_t part_id;
+ void *private;
+ int64_t (*read)(struct scom_controller *, uint32_t chip, uint64_t reg, uint64_t *val);
+ int64_t (*write)(struct scom_controller *, uint32_t chip, uint64_t reg, uint64_t val);
+
+ struct list_node link;
+};
+
+int64_t scom_register(struct scom_controller *new);
+
#endif /* __XSCOM_H */