192 lines
4.5 KiB
C
192 lines
4.5 KiB
C
/*
|
|
* 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,
|
|
};
|