1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
/*
* Copyright © 2017-2022 The Crust Firmware Authors.
* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only
*/
#include <debug.h>
#include <error.h>
#include <mmio.h>
#include <stdbool.h>
#include <stddef.h>
#include <util.h>
#include <clock/ccu.h>
#include <msgbox/sunxi-msgbox.h>
#include <platform/devices.h>
#include "msgbox.h"
#define CTRL_REG0 0x0000
#define CTRL_REG1 0x0004
#define CTRL_NORMAL 0x01100110
#define IRQ_EN_REG 0x0040
#define IRQ_STAT_REG 0x0050
#define REMOTE_IRQ_EN_REG 0x0060
#define REMOTE_IRQ_STAT_REG 0x0070
#define RX_IRQ(n) BIT(0 + 2 * (n))
#define RX_IRQ_MASK 0x5555
#define TX_IRQ(n) BIT(1 + 2 * (n))
#define TX_IRQ_MASK 0xaaaa
#define FIFO_STAT_REG(n) (0x0100 + 0x4 * (n))
#define FIFO_STAT_MASK BIT(0)
#define MSG_STAT_REG(n) (0x0140 + 0x4 * (n))
#define MSG_STAT_MASK GENMASK(2, 0)
#define MSG_DATA_REG(n) (0x0180 + 0x4 * (n))
static bool
sunxi_msgbox_peek_data(const struct device *dev, uint8_t chan)
{
const struct simple_device *self = to_simple_device(dev);
return mmio_read_32(self->regs + MSG_STAT_REG(chan)) & MSG_STAT_MASK;
}
static void
sunxi_msgbox_ack_rx(const struct device *dev, uint8_t chan)
{
const struct simple_device *self = to_simple_device(dev);
mmio_write_32(self->regs + IRQ_STAT_REG, RX_IRQ(chan));
}
static bool
sunxi_msgbox_last_tx_done(const struct device *dev, uint8_t chan)
{
const struct simple_device *self = to_simple_device(dev);
assert(chan < SUNXI_MSGBOX_CHANS);
return !(mmio_read_32(self->regs + REMOTE_IRQ_STAT_REG) &
RX_IRQ(chan));
}
static int
sunxi_msgbox_receive(const struct device *dev, uint8_t chan, uint32_t *msg)
{
const struct simple_device *self = to_simple_device(dev);
assert(chan < SUNXI_MSGBOX_CHANS);
/* Check if a new message is available before reading it. */
if (!sunxi_msgbox_peek_data(dev, chan))
return ENOENT;
*msg = mmio_read_32(self->regs + MSG_DATA_REG(chan));
return SUCCESS;
}
static int
sunxi_msgbox_send(const struct device *dev, uint8_t chan, uint32_t msg)
{
const struct simple_device *self = to_simple_device(dev);
assert(chan < SUNXI_MSGBOX_CHANS);
/* Reject the message if the FIFO is full. */
if (mmio_read_32(self->regs + FIFO_STAT_REG(chan)) & FIFO_STAT_MASK)
return EBUSY;
mmio_write_32(self->regs + MSG_DATA_REG(chan), msg);
return SUCCESS;
}
static int
sunxi_msgbox_probe(const struct device *dev)
{
const struct simple_device *self = to_simple_device(dev);
int err;
if ((err = simple_device_probe(dev)))
return err;
/* Set even channels ARM -> SCP and odd channels SCP -> ARM. */
mmio_write_32(self->regs + CTRL_REG0, CTRL_NORMAL);
mmio_write_32(self->regs + CTRL_REG1, CTRL_NORMAL);
/* Drain messages in RX channels (required to clear IRQs). */
for (uint8_t chan = 0; chan < SUNXI_MSGBOX_CHANS; chan += 2) {
while (sunxi_msgbox_peek_data(dev, chan))
mmio_read_32(self->regs + MSG_DATA_REG(chan));
}
/* Disable and clear all IRQ. */
mmio_write_32(self->regs + IRQ_EN_REG, 0);
mmio_write_32(self->regs + IRQ_STAT_REG, GENMASK(15, 0));
return SUCCESS;
}
static const struct msgbox_driver sunxi_msgbox_driver = {
.drv = {
.probe = sunxi_msgbox_probe,
.release = simple_device_release,
},
.ops = {
.ack_rx = sunxi_msgbox_ack_rx,
.last_tx_done = sunxi_msgbox_last_tx_done,
.receive = sunxi_msgbox_receive,
.send = sunxi_msgbox_send,
},
};
const struct simple_device msgbox = {
.dev = {
.name = "msgbox",
.drv = &sunxi_msgbox_driver.drv,
.state = DEVICE_STATE_INIT,
},
.clock = { .dev = &ccu.dev, .id = CLK_BUS_MSGBOX },
.regs = DEV_MSGBOX,
};
|