| /* |
| * 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, |
| }; |