]> git.baikalelectronics.ru Git - kernel.git/commitdiff
mailbox: qcom-ipcc: Dynamic alloc for channel arrangement
authorHuang Yiwei <quic_hyiwei@quicinc.com>
Mon, 22 Nov 2021 05:05:06 +0000 (13:05 +0800)
committerJassi Brar <jaswinder.singh@linaro.org>
Wed, 12 Jan 2022 05:47:33 +0000 (23:47 -0600)
Dynamic alloc for channel arrangement instead of static alloced
array, it is more flexible and can reduce memory usage.

Signed-off-by: Huang Yiwei <quic_hyiwei@quicinc.com>
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
drivers/mailbox/qcom-ipcc.c

index f1d4f4679b17bad3beb3c5ddd83b9efe0b65783f..10cb0b5461090efc728400190edfd41038e04241 100644 (file)
@@ -13,8 +13,6 @@
 
 #include <dt-bindings/mailbox/qcom-ipcc.h>
 
-#define IPCC_MBOX_MAX_CHAN             48
-
 /* IPCC Register offsets */
 #define IPCC_REG_SEND_ID               0x0c
 #define IPCC_REG_RECV_ID               0x10
@@ -52,9 +50,10 @@ struct qcom_ipcc {
        struct device *dev;
        void __iomem *base;
        struct irq_domain *irq_domain;
-       struct mbox_chan chan[IPCC_MBOX_MAX_CHAN];
-       struct qcom_ipcc_chan_info mchan[IPCC_MBOX_MAX_CHAN];
+       struct mbox_chan *chans;
+       struct qcom_ipcc_chan_info *mchan;
        struct mbox_controller mbox;
+       int num_chans;
        int irq;
 };
 
@@ -166,25 +165,37 @@ static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox,
        struct qcom_ipcc *ipcc = to_qcom_ipcc(mbox);
        struct qcom_ipcc_chan_info *mchan;
        struct mbox_chan *chan;
-       unsigned int i;
+       struct device *dev;
+       int chan_id;
+
+       dev = ipcc->dev;
 
        if (ph->args_count != 2)
                return ERR_PTR(-EINVAL);
 
-       for (i = 0; i < IPCC_MBOX_MAX_CHAN; i++) {
-               chan = &ipcc->chan[i];
-               if (!chan->con_priv) {
-                       mchan = &ipcc->mchan[i];
-                       mchan->client_id = ph->args[0];
-                       mchan->signal_id = ph->args[1];
-                       chan->con_priv = mchan;
-                       break;
-               }
+       for (chan_id = 0; chan_id < mbox->num_chans; chan_id++) {
+               chan = &ipcc->chans[chan_id];
+               mchan = chan->con_priv;
 
-               chan = NULL;
+               if (!mchan)
+                       break;
+               else if (mchan->client_id == ph->args[0] &&
+                               mchan->signal_id == ph->args[1])
+                       return ERR_PTR(-EBUSY);
        }
 
-       return chan ?: ERR_PTR(-EBUSY);
+       if (chan_id >= mbox->num_chans)
+               return ERR_PTR(-EBUSY);
+
+       mchan = devm_kzalloc(dev, sizeof(*mchan), GFP_KERNEL);
+       if (!mchan)
+               return ERR_PTR(-ENOMEM);
+
+       mchan->client_id = ph->args[0];
+       mchan->signal_id = ph->args[1];
+       chan->con_priv = mchan;
+
+       return chan;
 }
 
 static const struct mbox_chan_ops ipcc_mbox_chan_ops = {
@@ -192,15 +203,49 @@ static const struct mbox_chan_ops ipcc_mbox_chan_ops = {
        .shutdown = qcom_ipcc_mbox_shutdown,
 };
 
-static int qcom_ipcc_setup_mbox(struct qcom_ipcc *ipcc)
+static int qcom_ipcc_setup_mbox(struct qcom_ipcc *ipcc,
+                               struct device_node *controller_dn)
 {
+       struct of_phandle_args curr_ph;
+       struct device_node *client_dn;
        struct mbox_controller *mbox;
        struct device *dev = ipcc->dev;
+       int i, j, ret;
+
+       /*
+        * Find out the number of clients interested in this mailbox
+        * and create channels accordingly.
+        */
+       ipcc->num_chans = 0;
+       for_each_node_with_property(client_dn, "mboxes") {
+               if (!of_device_is_available(client_dn))
+                       continue;
+               i = of_count_phandle_with_args(client_dn,
+                                               "mboxes", "#mbox-cells");
+               for (j = 0; j < i; j++) {
+                       ret = of_parse_phandle_with_args(client_dn, "mboxes",
+                                               "#mbox-cells", j, &curr_ph);
+                       of_node_put(curr_ph.np);
+                       if (!ret && curr_ph.np == controller_dn) {
+                               ipcc->num_chans++;
+                               break;
+                       }
+               }
+       }
+
+       /* If no clients are found, skip registering as a mbox controller */
+       if (!ipcc->num_chans)
+               return 0;
+
+       ipcc->chans = devm_kcalloc(dev, ipcc->num_chans,
+                                       sizeof(struct mbox_chan), GFP_KERNEL);
+       if (!ipcc->chans)
+               return -ENOMEM;
 
        mbox = &ipcc->mbox;
        mbox->dev = dev;
-       mbox->num_chans = IPCC_MBOX_MAX_CHAN;
-       mbox->chans = ipcc->chan;
+       mbox->num_chans = ipcc->num_chans;
+       mbox->chans = ipcc->chans;
        mbox->ops = &ipcc_mbox_chan_ops;
        mbox->of_xlate = qcom_ipcc_mbox_xlate;
        mbox->txdone_irq = false;
@@ -233,7 +278,7 @@ static int qcom_ipcc_probe(struct platform_device *pdev)
        if (!ipcc->irq_domain)
                return -ENOMEM;
 
-       ret = qcom_ipcc_setup_mbox(ipcc);
+       ret = qcom_ipcc_setup_mbox(ipcc, pdev->dev.of_node);
        if (ret)
                goto err_mbox;
 
@@ -241,7 +286,7 @@ static int qcom_ipcc_probe(struct platform_device *pdev)
                               IRQF_TRIGGER_HIGH, "ipcc", ipcc);
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret);
-               goto err_mbox;
+               goto err_req_irq;
        }
 
        enable_irq_wake(ipcc->irq);
@@ -249,6 +294,9 @@ static int qcom_ipcc_probe(struct platform_device *pdev)
 
        return 0;
 
+err_req_irq:
+       if (ipcc->num_chans)
+               mbox_controller_unregister(&ipcc->mbox);
 err_mbox:
        irq_domain_remove(ipcc->irq_domain);