blob: d08f9d1500d4d6b0799752f16009d0731684b612 [file] [log] [blame] [edit]
/*
* 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,
};