diff options
author | Teo Hall <teo.hall@nxp.com> | 2016-09-12 14:15:44 -0500 |
---|---|---|
committer | Ye Li <ye.li@nxp.com> | 2018-04-27 02:32:30 -0700 |
commit | a360802ec1d1b52ea7d0c91e285ba355d0293140 (patch) | |
tree | ae9cd59cf62be1286b4075ded1307ac9e00e7e2c /drivers/mailbox | |
parent | afcfb7e5105ef01ec46a6c896b20e210a07ee094 (diff) |
MLK-14938-24 mailbox: add imx mu DM mailbox driver
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>
(cherry picked from commit 9caafe21ddf12c9ab994fe9e65dc7afe5e7bab3d)
Diffstat (limited to 'drivers/mailbox')
-rw-r--r-- | drivers/mailbox/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mailbox/Makefile | 1 | ||||
-rw-r--r-- | drivers/mailbox/imx-mu.c | 191 |
3 files changed, 199 insertions, 0 deletions
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 9649b70589..14b5a03c9a 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -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 diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 155dbeb099..75cd456678 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -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 index 0000000000..d08f9d1500 --- /dev/null +++ b/drivers/mailbox/imx-mu.c @@ -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, +}; |