MLK-14938-24 mailbox: add imx mu DM mailbox driver
authorTeo Hall <teo.hall@nxp.com>
Mon, 12 Sep 2016 19:15:44 +0000 (14:15 -0500)
committerJason Liu <jason.hui.liu@nxp.com>
Thu, 2 Nov 2017 18:36:51 +0000 (02:36 +0800)
implement i.MX Messaging Unit driver within the DM Mailbox uclass

Signed-off-by: Teo Hall <teo.hall@nxp.com>
Signed-off-by: Ye Li <ye.li@nxp.com>
drivers/mailbox/Kconfig
drivers/mailbox/Makefile
drivers/mailbox/imx-mu.c [new file with mode: 0644]

index 9649b70..14b5a03 100644 (file)
@@ -24,4 +24,11 @@ config TEGRA_HSP
          This enables support for the NVIDIA Tegra HSP Hw module, which
          implements doorbells, mailboxes, semaphores, and shared interrupts.
 
+config IMX_MU
+        bool "Enable i.MX MU support"
+        depends on DM_MAILBOX
+        help
+          Enable support for i.MX Messaging Unit for communication with other
+          processors on the SoC
+
 endmenu
index 155dbeb..75cd456 100644 (file)
@@ -6,3 +6,4 @@ obj-$(CONFIG_DM_MAILBOX) += mailbox-uclass.o
 obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o
 obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o
 obj-$(CONFIG_TEGRA_HSP) += tegra-hsp.o
+obj-$(CONFIG_IMX_MU) += imx-mu.o
diff --git a/drivers/mailbox/imx-mu.c b/drivers/mailbox/imx-mu.c
new file mode 100644 (file)
index 0000000..d08f9d1
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <mailbox-uclass.h>
+
+#define NUM_MU_CHANNELS     4
+#define NUM_MU_FLAGS        4
+#define NUM_MU_GIP          4
+
+#define mu_rr(x)            (0x10 + (x * 0x4))
+#define mu_tr(x)            (x * 0x4)
+#define MU_SR_OFFSET        0x20
+#define MU_CR_OFFSET        0x24
+#define CHAN_TE_MASK(x)     (0x00100000 << (x))
+#define CHAN_RF_MASK(x)     (0x01000000 << (x))
+#define MU_CR_INT_MSK       0xFFF00000
+#define MU_FLGS_MSK         0x00000007
+#define MU_GIP_MSK          0xF0000000
+
+
+
+/* This driver only exposes the status bits to keep with the
+ * polling methodology of u-boot.
+ */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct imx_mu_mbox {
+       fdt_addr_t base;
+
+       /* use pointers to channel as a way to reserve channels */
+       void *channels[NUM_MU_CHANNELS];
+       bool flags[NUM_MU_FLAGS];
+
+       /* TODO add support for the reading/setting of flags to
+        * B side of MU
+        */
+};
+
+
+/* check that the channel is open or owned by caller */
+static int mu_check_channel(struct mbox_chan *chan)
+{
+       struct imx_mu_mbox *mailbox = dev_get_priv(chan->dev);
+
+       /* use id as number of channel within mbox only */
+       if ((chan->id < 0) || (chan->id >= NUM_MU_CHANNELS)) {
+               debug("nxp mu id out of range: %lu\n", chan->id);
+               return -EINVAL;
+       }
+       if (mailbox->channels[chan->id] != NULL) {
+               /* if reserved check that caller owns */
+               if (mailbox->channels[chan->id] == chan)
+                       return 1; /* caller owns the channel */
+
+               return -EACCES;
+       }
+       return 0;/* channel empty */
+}
+
+static int mu_chan_request(struct mbox_chan *chan)
+{
+       struct imx_mu_mbox *mailbox = dev_get_priv(chan->dev);
+
+       debug("%s(chan=%p)\n", __func__, chan);
+
+       int status = mu_check_channel(chan);
+       if (status < 0) {
+               debug("channel not available :%d\n", status);
+               return -EPERM;
+       }
+       mailbox->channels[chan->id] = chan;
+
+       return 0;
+}
+/* currently not dynamically allocated
+ * only change pointer back to NULL */
+static int mu_chan_free(struct mbox_chan *chan)
+{
+       struct imx_mu_mbox *mailbox = dev_get_priv(chan->dev);
+       int status = mu_check_channel(chan);
+
+       debug("%s(chan=%p)\n", __func__, chan);
+       if (status <= 0) { /* check that the channel is also not empty */
+               debug("mu_chan_free() failed exit code: %d\n", status);
+               return status;
+       }
+       /*if you own channel and  channel is NOT empty */
+       mailbox->channels[chan->id] = NULL;
+
+       return 0;
+}
+
+static int mu_send(struct mbox_chan *chan, const void *data)
+{
+       struct imx_mu_mbox *mbox = dev_get_priv(chan->dev);
+       int status = mu_check_channel(chan);
+       uint32_t val = *((uint32_t *)data);
+
+       debug("%s(chan=%p, data=%p)\n", __func__, chan, data);
+       if (status < 1) {
+               debug("mu_send() failed. mu_chan_status is :%d\n", status);
+               return -EPERM;
+       }
+
+       /*check if transmit register is empty */
+       if (!(readl(mbox->base+MU_SR_OFFSET) & CHAN_TE_MASK(chan->id)))
+               return -EBUSY;
+
+       /* send out on transmit register*/
+       writel(val, mbox->base + mu_tr(chan->id));
+       return 0;
+}
+
+static int mu_recv(struct mbox_chan *chan, void *data)
+{
+       struct imx_mu_mbox *mbox = dev_get_priv(chan->dev);
+       int status = mu_check_channel(chan);
+       uint32_t *buffer =  data;
+
+       debug("%s(chan=%p, data=%p)\n", __func__, chan, data);
+
+       if (status < 1)
+               return -EPERM; /* return if channel isnt owned */
+
+       if (readl(mbox->base + MU_SR_OFFSET) & CHAN_RF_MASK(chan->id))
+               return -ENODATA;
+
+       *buffer = readl(mu_rr(chan->id));
+
+       return 0;
+}
+
+static int imx_mu_bind(struct udevice *dev)
+{
+       debug("%s(dev=%p)\n", __func__, dev);
+
+       return 0;
+}
+
+static int imx_mu_probe(struct udevice *dev)
+{
+       struct imx_mu_mbox *mbox = dev_get_priv(dev);
+       uint32_t val;
+       debug("%s(dev=%p)\n", __func__, dev);
+
+       /* get address from device tree */
+       mbox->base = dev_get_addr(dev);
+       if (mbox->base == FDT_ADDR_T_NONE)
+               return -ENODEV;
+
+       val = readl(mbox->base + MU_CR_OFFSET);
+       val = val & ~MU_CR_INT_MSK;/* disable all interrupts */
+       val = val & ~MU_FLGS_MSK; /* clear all flags */
+
+       writel(val, mbox->base + MU_CR_OFFSET);
+
+       val = readl(mbox->base + MU_SR_OFFSET);
+       val = val | MU_GIP_MSK;   /* clear any pending GIP */
+       writel(val, mbox->base + MU_SR_OFFSET);
+
+       return 0;
+}
+
+static const struct udevice_id imx_mu_ids[] = {
+       { .compatible = "nxp,imx-mu" },
+       { }
+};
+
+struct mbox_ops imx_mu_mbox_ops = {
+       .request = mu_chan_request,
+       .free = mu_chan_free,
+       .send = mu_send,
+       .recv = mu_recv,
+};
+
+U_BOOT_DRIVER(imx_mu) = {
+       .name = "imx-mu",
+       .id = UCLASS_MAILBOX,
+       .of_match = imx_mu_ids,
+       .bind = imx_mu_bind,
+       .probe = imx_mu_probe,
+       .priv_auto_alloc_size = sizeof(struct imx_mu_mbox),
+       .ops = &imx_mu_mbox_ops,
+};