libtx2bpmp: Share initialised BPMP structures

There are now multiple interfaces that require access to the BPMP and it
isn't possible to have multiple copies of BPMP state as the BPMP's
private state must be shared. Thus, we now keep a global BPMP structure
and share that instead of requiring each user to initialise a private
structure. This commit also updates the tx2 clock driver to reflect
those changes.
diff --git a/libplatsupportports/src/plat/tx2/clock.c b/libplatsupportports/src/plat/tx2/clock.c
index 9bd1213..30e5b22 100644
--- a/libplatsupportports/src/plat/tx2/clock.c
+++ b/libplatsupportports/src/plat/tx2/clock.c
@@ -34,7 +34,7 @@
 typedef struct tx2_clk {
     ps_io_ops_t *io_ops;
     void *car_vaddr;
-    struct tx2_bpmp bpmp;
+    struct tx2_bpmp *bpmp;
 } tx2_clk_t;
 
 static inline bool check_valid_gate(enum clock_gate gate)
@@ -68,7 +68,7 @@
     struct mrq_clk_response res = {0};
     tx2_clk_t *clk = clock_sys->priv;
 
-    int bytes_recvd = tx2_bpmp_call(&clk->bpmp, MRQ_CLK, &req, sizeof(req), &res, sizeof(res));
+    int bytes_recvd = tx2_bpmp_call(clk->bpmp, MRQ_CLK, &req, sizeof(req), &res, sizeof(res));
     if (bytes_recvd < 0) {
         return -EIO;
     }
@@ -82,7 +82,7 @@
     struct mrq_clk_response res = {0};
     tx2_clk_t *tx2_clk = clk->clk_sys->priv;
 
-    int bytes_recvd = tx2_bpmp_call(&tx2_clk->bpmp, MRQ_CLK, &req, sizeof(req), &res, sizeof(&res));
+    int bytes_recvd = tx2_bpmp_call(tx2_clk->bpmp, MRQ_CLK, &req, sizeof(req), &res, sizeof(&res));
     if (bytes_recvd < 0) {
         return 0;
     }
@@ -97,7 +97,7 @@
     struct mrq_clk_response res = {0};
     tx2_clk_t *tx2_clk = clk->clk_sys->priv;
 
-    int bytes_recvd = tx2_bpmp_call(&tx2_clk->bpmp, MRQ_CLK, &req, sizeof(req), &res, sizeof(&res));
+    int bytes_recvd = tx2_bpmp_call(tx2_clk->bpmp, MRQ_CLK, &req, sizeof(req), &res, sizeof(&res));
     if (bytes_recvd < 0) {
         return 0;
     }
@@ -139,7 +139,7 @@
     struct mrq_clk_request req = { .cmd_and_id = (CMD_CLK_GET_ALL_INFO << 24) | bpmp_clk_id };
     struct mrq_clk_response res = {0};
     char *clock_name = NULL;
-    int bytes_recvd = tx2_bpmp_call(&tx2_clk->bpmp, MRQ_CLK, &req, sizeof(req), &res, sizeof(res));
+    int bytes_recvd = tx2_bpmp_call(tx2_clk->bpmp, MRQ_CLK, &req, sizeof(req), &res, sizeof(res));
     if (bytes_recvd < 0) {
         ZF_LOGE("Failed to initialise the clock");
         goto fail;
@@ -242,7 +242,7 @@
     }
 
     if (bpmp_initialised) {
-        ZF_LOGF_IF(tx2_bpmp_destroy(io_ops, &clk->bpmp),
+        ZF_LOGF_IF(tx2_bpmp_destroy(io_ops, clk->bpmp),
                    "Failed to cleanup after a failed clock system initialisation");
     }
 
diff --git a/libtx2bpmp/include/tx2bpmp/bpmp.h b/libtx2bpmp/include/tx2bpmp/bpmp.h
index 13d3847..a6575ff 100644
--- a/libtx2bpmp/include/tx2bpmp/bpmp.h
+++ b/libtx2bpmp/include/tx2bpmp/bpmp.h
@@ -51,23 +51,19 @@
 #define TX2_BPMP_RX_SHMEM_PADDR 0x3004f000
 #define TX2_BPMP_RX_SHMEM_SIZE 0x1000
 
-struct tx2_bpmp {
-    tx2_hsp_t hsp;
-    bool hsp_initialised;
-    struct tegra_ivc ivc;
-    void *tx_base; // Virtual address base of the TX shared memory channel
-    void *rx_base; // Virtual address base of the RX shared memory channel
-};
+/* Forward declare the tx2_bpmp struct, this struct is private to the bpmp
+ * source file. */
+struct tx2_bpmp;
 
 /*
  * Initialises the BPMP interfaces.
  *
  * @param io_ops Initialised IO ops interface.
- * @param bpmp Empty tx2_bpmp struct that will be filled in.
+ * @param bpmp Pointer to a BPMP struct pointer that will be filled in.
  *
  * @return 0 on success, otherwise an error code.
  */
-int tx2_bpmp_init(ps_io_ops_t *io_ops, struct tx2_bpmp *bpmp);
+int tx2_bpmp_init(ps_io_ops_t *io_ops, struct tx2_bpmp **bpmp);
 
 /*
  * Destroys an initialised BPMP interface.
diff --git a/libtx2bpmp/src/bpmp.c b/libtx2bpmp/src/bpmp.c
index 6ba37ea..b474fbf 100644
--- a/libtx2bpmp/src/bpmp.c
+++ b/libtx2bpmp/src/bpmp.c
@@ -37,6 +37,14 @@
 
 #define TIMEOUT_THRESHOLD 2000000ul
 
+struct tx2_bpmp {
+    tx2_hsp_t hsp;
+    bool hsp_initialised;
+    struct tegra_ivc ivc;
+    void *tx_base; // Virtual address base of the TX shared memory channel
+    void *rx_base; // Virtual address base of the RX shared memory channel
+};
+
 pmem_region_t bpmp_shmems[NUM_SHMEM] = {
     {
         .type = PMEM_TYPE_DEVICE,
@@ -50,6 +58,10 @@
     }
 };
 
+static bool bpmp_initialised = false;
+static unsigned int bpmp_refcount = 0;
+static struct tx2_bpmp bpmp_data = {0};
+
 int tx2_bpmp_call(struct tx2_bpmp *bpmp, int mrq, void *tx_msg, size_t tx_size, void *rx_msg, size_t rx_size)
 {
 	int ret, err;
@@ -125,49 +137,57 @@
 		ZF_LOGF("Failed to ring BPMP's doorbell in the HSP: %d\n", ret);
 }
 
-int tx2_bpmp_init(ps_io_ops_t *io_ops, struct tx2_bpmp *bpmp)
+int tx2_bpmp_init(ps_io_ops_t *io_ops, struct tx2_bpmp **bpmp)
 {
     if (!io_ops || !bpmp) {
         ZF_LOGE("Arguments are NULL!");
         return -EINVAL;
     }
 
+    if (bpmp_initialised) {
+        /* If we've initialised the BPMP once, just return the initialised
+         * structure */
+        *bpmp = &bpmp_data;
+        bpmp_refcount++;
+        return 0;
+    }
+
     int ret = 0;
     /* Not sure if this is too long or too short. */
     unsigned long timeout = TIMEOUT_THRESHOLD;
 
-    ret = tx2_hsp_init(io_ops, &bpmp->hsp);
+    ret = tx2_hsp_init(io_ops, &bpmp_data.hsp);
     if (ret) {
         ZF_LOGE("Failed to initialise the HSP device for BPMP");
         return ret;
     }
 
-    bpmp->hsp_initialised = true;
+    bpmp_data.hsp_initialised = true;
 
-    bpmp->tx_base = ps_pmem_map(io_ops, bpmp_shmems[TX_SHMEM], false, PS_MEM_NORMAL);
-    if (!bpmp->tx_base) {
+    bpmp_data.tx_base = ps_pmem_map(io_ops, bpmp_shmems[TX_SHMEM], false, PS_MEM_NORMAL);
+    if (!bpmp_data.tx_base) {
         ZF_LOGE("Failed to map the TX BPMP channel");
         ret = -ENOMEM;
         goto fail;
     }
 
-    bpmp->rx_base = ps_pmem_map(io_ops, bpmp_shmems[RX_SHMEM], false, PS_MEM_NORMAL);
-    if (!bpmp->rx_base) {
+    bpmp_data.rx_base = ps_pmem_map(io_ops, bpmp_shmems[RX_SHMEM], false, PS_MEM_NORMAL);
+    if (!bpmp_data.rx_base) {
         ZF_LOGE("Failed to map the RX BPMP channel");
         ret = -ENOMEM;
         goto fail;
     }
 
-    ret = tegra_ivc_init(&bpmp->ivc, (unsigned long) bpmp->rx_base, (unsigned long) bpmp->tx_base,
-                         BPMP_IVC_FRAME_COUNT, BPMP_IVC_FRAME_SIZE, tx2_bpmp_ivc_notify, (void *) bpmp);
+    ret = tegra_ivc_init(&bpmp_data.ivc, (unsigned long) bpmp_data.rx_base, (unsigned long) bpmp_data.tx_base,
+                         BPMP_IVC_FRAME_COUNT, BPMP_IVC_FRAME_SIZE, tx2_bpmp_ivc_notify, (void *) &bpmp_data);
     if (ret) {
         ZF_LOGE("tegra_ivc_init() failed: %d", ret);
         goto fail;
     }
 
-    tegra_ivc_channel_reset(&bpmp->ivc);
+    tegra_ivc_channel_reset(&bpmp_data.ivc);
     for (; timeout > 0; timeout--) {
-        ret = tegra_ivc_channel_notified(&bpmp->ivc);
+        ret = tegra_ivc_channel_notified(&bpmp_data.ivc);
         if (!ret) {
             break;
         }
@@ -179,10 +199,14 @@
         goto fail;
     }
 
+    *bpmp = &bpmp_data;
+    bpmp_refcount++;
+    bpmp_initialised = true;
+
     return 0;
 
 fail:
-    ZF_LOGF_IF(tx2_bpmp_destroy(io_ops, bpmp), "Failed to cleanup the BPMP after a failed initialisation");
+    ZF_LOGF_IF(tx2_bpmp_destroy(io_ops, &bpmp_data), "Failed to cleanup the BPMP after a failed initialisation");
     return ret;
 }
 
@@ -193,18 +217,25 @@
         return -EINVAL;
     }
 
-    if (bpmp->hsp_initialised) {
-        ZF_LOGF_IF(tx2_hsp_destroy(io_ops, &bpmp->hsp),
+    bpmp_refcount--;
+
+    if (bpmp_refcount != 0) {
+        /* Only cleanup the BPMP structure if there are no more references that are valid. */
+        return 0;
+    }
+
+    if (bpmp_data.hsp_initialised) {
+        ZF_LOGF_IF(tx2_hsp_destroy(io_ops, &bpmp_data.hsp),
                    "Failed to clean up after a failed BPMP initialisation process!");
     }
 
     /* Unmapping the shared memory also destroys the IVC */
-    if (bpmp->tx_base) {
-        ps_io_unmap(&io_ops->io_mapper, bpmp->tx_base, bpmp_shmems[TX_SHMEM].length);
+    if (bpmp_data.tx_base) {
+        ps_io_unmap(&io_ops->io_mapper, bpmp_data.tx_base, bpmp_shmems[TX_SHMEM].length);
     }
 
-    if (bpmp->rx_base) {
-        ps_io_unmap(&io_ops->io_mapper, bpmp->tx_base, bpmp_shmems[RX_SHMEM].length);
+    if (bpmp_data.rx_base) {
+        ps_io_unmap(&io_ops->io_mapper, bpmp_data.tx_base, bpmp_shmems[RX_SHMEM].length);
     }
 
     return 0;